LinkedList
的底层实现是一个双向链表,它是接口List
和Deque
的一个实现类;LinkedList
是非同步的;LinkedList
的iterator
和listIterator
方法返回的迭代器是fail-fast的。public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
与ArrayList
相比而言多实现了Deque
接口,继承的抽象类变为了AbstractList
的子类AbstractSequentialList
,并且减少了RandomAccess
接口的实现。
仅介绍部分。
LinkedList
的底层由一个双向链表实现,其定义为:
private static class Node<E> {
E item; //节点存储的内容
Node<E> next; //对下个节点的引用
Node<E> prev; //对前个节点的引用
}
其余一些使用频率较高的成员变量:
transient int size = 0; //集合大小
transient Node<E> first;//指向头结点的引用
transient Node<E> last;//指向尾结点的引用
public LinkedList();
,无参构造方法;public LinkedList(Collection extends E> c);
,构造一个包含指定集合元素的列表,其顺序由集合的迭代器返回。public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c); //将c中元素依次添加进集合
}
接下来将介绍一些辅助方法,比较重要,LinkedList
中通过其完成大部分List
和Deque
操作,如Add、remove、offer
等方法。
private void linkFirst(E e) {
//头插元素
final Node<E> f = first; //临时保存下当前头结点
final Node<E> newNode = new Node<>(null, e, f); //创建新节点,并将新节点的next指向原头结点
first = newNode; //该节点为新头结点
if (f == null) //如果原头结点为null,则说明原集合为空,
last = newNode; //插入后集合中仅有一个元素,头、尾节点指向同一节点
else
f.prev = newNode;//将原头结点的prev指向新头结点
size++; //更新集合大小
modCount++;
}
void linkLast(E e); //这个是尾插法,与头插法类似,不过多赘述
void linkBefore(E e, Node<E> succ) {
//在制定位置succ之前插入节点,即固定位置插入节点
// assert succ != null;
final Node<E> pred = succ.prev; //临时保存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++;
}
private E unlinkFirst(Node<E> f) {
//删除头结点
// assert f == first && f != null;
final E element = f.item; //存下要删节点的内容,即方法返回值
final Node<E> next = f.next; //新头结点
f.item = null; //删除节点
f.next = null; // help GC
first = next;
if (next == null) //删除后集合为空
last = null;
else
next.prev = null; //否则头结点的prev为null
size--;//更新集合大小
modCount++;
return element;
}
private E unlinkLast(Node<E> l);//删除尾结点,与上述方法类似
E unlink(Node<E> x) {
//删除给定元素
// assert x != null;
final E element = x.item;//存下要删节点的内容,即方法返回值
final Node<E> next = x.next;//分别存下当前节点的前后节点
final Node<E> prev = x.prev;
if (prev == null) {
//代表要删除节点为头结点
first = next; //赋值新头结点
} else {
//否则直接断开原连接
prev.next = next;
x.prev = null;
}
if (next == null) {
//代表要删除节点为尾结点
last = prev; //赋值新尾结点
} else {
//否则直接断开原连接
next.prev = prev;
x.next = null;
}
x.item = null; //删除节点
size--;
modCount++;
return element;
}
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;
}
}
与ArrayList
很接近(不考虑实现的话),具体可见博客
这里主要介绍一下与ArrayL
不同的,即Deque
接口的一些实现方法。
以下两组都可以将集合作为队列来操作,两组操作的区别:
List
的实现,后者属于queue
的实现;offer、poll、peek
相对比较友好,遇到队列为空等情况不会报错,而是有返回值,而前一组抛出错误;List
使用时,一般采用add、get
方法,作为Queue
使用时,才会采用 offer、poll、peek
等方法;boolean add(E e); //将元素插入队列
E remove();//将队首的元素删除,队列为空则抛出异常
E element();//获取队首元素,但不移除,队列为空则抛出异常
boolean offer(E e);//将元素插入队列
E poll();//将队首的元素删除,队列为空则返回null
E peek();//获取队首元素,但不移除,队列为空则返回null
其余操作,可相互组合,当做栈或队列使用。
public boolean offerFirst(E e); //存
public boolean offerLast(E e);
public E peekFirst(); //取
public E peekLast();
public E pollFirst(); //删
public E pollLast();
//还有一对操作push、pop,可直接当做栈来使用
public void push(E e) {
addFirst(e);
}
public E pop() {
return removeFirst();
}
LinkedList
由于本质是链表,相对于ArrayList
而言,插入或删除元素时不再需要对元素进行挪动,所以它在数据的删改方面会很快(只指插入删除这一个动作),而在查询或修改元素过程时,不能像数组一下通过下标访问,每次需要遍历链表进行查找,这样就比ArrayList
慢很多。
综上LinkedList
,在增删(在List
前段位置)操作较多,查询、修改较少时比较合适。