说明
List 和 code Deque 接口的双向链表实现。实现所有可选的列表操作,并允许所有元素(包括null)。
所有操作的执行都符合双向链表的预期。索引到列表中的操作将从开始或结束遍历列表,以更接近指定索引的为准。
注意这个实现不是同步的。如果多个线程并发访问一个链表,并且至少有一个线程在结构上修改了链表,它必须在外部同步。 (结构修改是添加或删除一个或多个元素的任何操作;仅设置元素的值不是结构修改。)这通常是通过同步一些自然封装列表的对象来完成的。如果不存在此类对象,则应使用CollectionssynchronizedList Collections.synchronizedList 方法“包装”该列表。这最好在创建时完成,以防止对列表的意外不同步访问: List list = Collections.synchronizedList(new LinkedList(...));
这个类的 返回的迭代器code iterator 和 e listIterator 方法是 fail-fast:如果在创建迭代器后的任何时间以任何方式修改列表的结构,除了通过 Iterator 自己的 remove 或 add 方法,迭代器将抛出一个 ConcurrentModificationException。因此,面对并发修改,迭代器快速而干净地失败,而不是冒着在未来不确定的时间出现任意、非确定性行为的风险。
请注意,无法保证迭代器的快速失败行为,因为一般来说,在存在非同步并发修改的情况下不可能做出任何硬性保证。快速失败的迭代器会尽最大努力抛出 ConcurrentModificationException。因此,编写一个依赖此异常来确保其正确性的程序是错误的:迭代器的快速失败行为应该仅用于检测错误。
源码分析(部分)
transient int size = 0;
/**
* 头结点
*/
transient Node<E> first;
/**
* 尾节点
*/
transient Node<E> last;
/**
* 无参数的构造器
*/
public LinkedList() {
}
/**
* 带有一个集合参数的构造器
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
/**
* 头插法,就是将e放在链表的第一个
*/
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
/**
* 尾插法,就是将要放入的元素放在最后一位
*/
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++;
}
/**
* 在固定元素前插入一个元素
*/
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++;
}
/**
*取消链接非空的第一个节点 f
*/
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;
size--;
modCount++;
return element;
}
/**
* 取消链接非空的最后一个节点 f。
*/
private E unlinkLast(Node<E> l) {
// assert l == last && l != null;
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
/**
* 取消链接非空节点 x。
*/
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;
}
/**
* 获得链表的第一个元素
*/
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
/**
* 获得链表最后一个元素
*/
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
/**
* 移除链表一个元素
*/
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
/**
* 移除链表最后一份节点
*/
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
/**
* 在头部插入节点
*/
public void addFirst(E e) {
linkFirst(e);
}
/**
* 在尾部加入节点
*/
public void addLast(E e) {
linkLast(e);
}
/**
*判断链表中是否存在特定元素
*/
public boolean contains(Object o) {
return indexOf(o) != -1;
}
/**
*返回链表中元素的数量
*/
public int size() {
return size;
}
/**
* 判断在尾部能否添加该元素
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
*判断呢能否移除相关元素,返回true表示已经移除
*/
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
/**
* Appends all of the elements in the specified collection to the end of
* this list, in the order that they are returned by the specified
* collection's iterator. The behavior of this operation is undefined if
* the specified collection is modified while the operation is in
* progress. (Note that this will occur if the specified collection is
* this list, and it's nonempty.)
*
* @param c collection containing elements to be added to this list
* @return {@code true} if this list changed as a result of the call
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
LinkedList总结
- LinkedList是一个功能很强大的类,可以被当作List集合,双端队列和栈来使用。
- LinkedList底层使用链表来保存集合中的元素,因此随机访问的性能较差,但是插入删除时性能非常的出色。
- LinkedList在1.8版本有添加了一点新的内容,添加了一个static final 修饰的内部类LLSpliterator 并实现了Spliterator ,为了实现并行遍历而新添加的功能,整体的变化并不是很大,感兴趣的可以自己去看一下。
- List实现类的使用场景
ArrayList,底层采用数组实现,如果需要遍历集合元素,应该使用随机访问的方式,对于LinkedList集合应该采用迭代器的方式
如果需要经常的插入。删除操作可以考虑使用LinkedList集合
如果有多个线程需要同时访问List集合中的元素,开发者可以考虑使用Collections将集合包装成线程安全的集合。 - 补充说明
从源码中很明显可以看出,LinkedList的实现是基于双向循环链表的,且头结点中不存放数据。
注意两个不同的构造方法。无参构造方法直接建立一个仅包含head节点的空链表,包含Collection的构造方法,先调用无参构造方法建立一个空链表,而后将Collection中的数据加入到链表的尾部后面。
在查找和删除某元素时,源码中都划分为该元素为null和不为null两种情况来处理,LinkedList中允许元素为null。
LinkedList是基于链表实现的,因此不存在容量不足的问题,所以这里没有扩容的方法。
注意源码中的Entry entry(int index)方法。该方法返回双向链表中指定位置处的节点,而链表中是没有下标索引的,要指定位置出的元素,就要遍历该链表,从源码的实现中,我们看到这里有一个加速动作。源码中先将index与长度size的一半比较,如果index<size/2,就只从位置0往后遍历到位置index处,而如果index>size/2,就只从位置size往前遍历到位置index处。这样可以减少一部分不必要的遍历,从而提高一定的效率(实际上效率还是很低)。
注意链表类对应的数据结构Entry。
LinkedList是基于链表实现的,因此插入删除效率高,查找效率低(虽然有一个加速动作)。
要注意源码中还实现了栈和队列的操作方法,因此也可以作为栈、队列和双端队列来使用。