LinkedList简介
- LinkedList是一个双向链表。它继承了AbstractSequentialList,可以被当作堆栈、队列或双端队列操作
- 实现了Lis能对它进行队列操作
- 实现了Deque接口能将LinkedList当作双端队列使用。
- LinkedList 实现了Cloneable接口可以被克隆。
- LinkedList实现java.io.Serializable接口,因此它也支持序列化,能够通过序列化传输。
- 与ArrayList一样不是同步的。
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
- 为什么继承AbstractSequentialList?
AbstractSequentialList中已经实现了get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index)这些基本操作,LinkedList实现了List,也就相当于实现了get(int index)从而最大限度地减少了实现受“连续访问”数据存储(如链接列表)支持的此接口所需的工作,减少List实现复杂度。
LinkedList实现原理
- 1.私有属性
transient int size = 0;
/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
* (first.prev == null && first.item != null)
*/
transient Node<E> first;
/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
* (last.next == null && last.item != null)
*/
transient Node<E> last;
- 2.构造函数
/**
* 构造一个空列表
*/
public LinkedList() {
}
/**
* 构造一个包含指定 collection 中的元素的列表,这些元素按其 collection 的迭代器返回的顺序排列
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
- 3.add(E e)
该方法是在链表的 end 添加元素,其调用了自己的方法 linkLast(E e)。
该方法首先将 last 的 Node 引用指向了一个新的 Node(l),然后根据l新建了一个 newNode,其中的元素就为要添加的 e;而后,我们让 last 指向了 newNode。接下来是自身进行维护该链表。
/**
* 在链表后面增加元素
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
* Links e as last element.
*/
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
- 4.add(int index, E element)
/**
* 在指定 index 位置插入元素。如果 index 位置正好等于 size,则调用 linkLast(element) 将其插入末尾;否则调用 linkBefore(element, node(index))方法进行插入。
*/
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
- 4.addAll(Collection
/**
* 添加指定 collection 中的所有元素到此列表的结尾,顺序是指定 collection 的迭代器返回这些元素的顺序。
*/
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
/**
* 将collection中的数据从集合index位置开始插入
*/
public boolean addAll(int index, Collection<? extends E> c) {
//检验将要插入index的位置是否合法
checkPositionIndex(index);
Object[] a = c.toArray();
int numNew = a.length;
//若插入的元素为空,则返回false
if (numNew == 0)
return false;
//succ用来保存index处的节点。插入位置如果是size,则在头结点前面插入,否则在获取index处的节点插入, pred用来获取前一个节点,插入时需要修改这个节点的next引用
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
// 按顺序将a数组中的第一个元素插入到index处,将之后的元素插在这个元素后面
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
//判断是否到最后一个节点
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
/**
* 二分法查找获取index处的节点,如果index大于size的一半,从后往前找;否则从前往后找
*/
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
- 5.获取元素
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
ArrayList和LinkedList的区别:
- ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
- 对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
- 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
ArrayList和LinkedList是两个集合类,用于存储一系列的对象引用(references)。例如我们可以用ArrayList来存储一系列的String或者Integer。