概述
继承结构
类描述
如继承结构所示,LinkedList是两个接口(List和Deque)的混合实现。其实现了List接口中所有的可选操作,并且LinkedList允许所有元素(包括null)的插入和访问操作。LinkedList的所有操作均基于双向链表实现,当根据下标对链表中的元素进行访问,将通过从头至尾或者从尾至头遍历链表中的所有元素,来到达指定位置。
LinkedList和ArrayList都不是线程安全的。当多个线程同时访问一个链表时,如果有一个对链表的结构进行了修改,则必须在外部显示的进行同步的处理。一般情况下,通过对包含本链表的对象进行同步处理。如果没有这种类型的对象,则可以通过如下方式生成一个线程安全的链表:
List list = Collections.synchronizedList(new LinkedList(...));
该类中的iterator
和listIterator
返回的迭代器仍然采用快速失败(fail-fast)机制:当链表由于调用除该迭代器的remove
和add
方法外,产生了结构行修改,该迭代器将抛出一个同步修改异常(ConcurrentModificationException
)。因此,当发生同步修改时,迭代器将不会在未来的某个时间里,返回潜在的脏数组,或者产生不可预期的行为。
注意:快速失败机制并不会保证方法生非同步并发修改时行为的正确性(我想到的情况是:在两个进程进行列表结构修改操作的时间相同,且对方法中进行失败机制校验的方法调用时间也极其接近时,可能出现两个进程均通过同步修改校验, 且均对链表进行了结构行修改),这样对于这两个进程,仍然认为链表的是同步的,其实已经产生了脏数据。快速失败机制只是在保证检测到同步修改时,抛出异常,证明脏数据存在的可能。因此,在编码过程中最好不要依赖同步修改异常做逻辑处理,但快速失败机制一般可用于调试bug。
内部类介绍
数据节点类(Node)
源码展示
private static class Node<E> {
E item; // 链表真实数据节点
Node<E> next; // 指向链表中下一个元素的指针
Node<E> prev; // 指向链表中上一个元素的指针
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
类说明
Node类为一个静态内部类,其不需要外部对象即可初始化。由Node的结构及属性可以看出,LinkedList的数据存储结构与C
语言中双向链表形式相同。每个节点除持有该节点的有效数据外,还需要持有指向上个节点和下个节点的指针,因此其单个节点占用的空间高于ArrayList中采用数组存储的节点。
列表迭代器类(ListItr)
属性介绍
private Node<E> lastReturned;
用户最后一次调用next()
或者previous
返回的节点;
private Node<E> next;
用户下次调用next()
返回的节点;
private int nextIndex;
用户下次调用next()
返回的节点的下标值。
private int expectedModCount = modCount;
迭代器创建时,其持有的链表对象发生结构性修改的次数,主要用于并发修改的校验;
构造方法
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
生成一个由链表特定位置开始的列表迭代器。当用户传入的index(有效下标为0 ~ size-1)与链表长度size相等时,则代表调用next()方法时,将不会返回任何有效元素(抛出异常或者返回null)。
方法介绍
public boolean hasNext() {
return nextIndex < size;
}
判断链表迭代器下次调用next方法时,是否还能返回有效的元素。由于链表中元素的有效下标范围时0 ~ size-1,因此当迭代器游标位置小于链表长度size时,则返回true,否则返回false。
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
返回列表迭代器当前游标对应的元素。在进行元素访问之前,需要进行并发修改的校验。由代码可以看出,LinkedList迭代器的原生迭代器抛出异常来代表链表中不再有有效元素,而不是返回一个特殊值(null)。
public boolean hasPrevious() {
return nextIndex > 0;
}
判断列表迭代器下次调用previous
时,是否还能返回有效的元素。当列表迭代器的游标大于0时,则代表列表迭代器可以向前遍历,返回true,否则返回false。
public E previous() {
checkForComodification();
if (!hasPrevious())
throw new NoSuchElementException();
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
}
返回迭代器当前游标的前一个元素。注意,在调用本方法时,当迭代器位于链表的尾端时,需要特殊处理。因为迭代器位于链表尾端时,迭代器中的的next对应的节点为null。
public int nextIndex() {
return nextIndex;
}
返回链表下次调用next()
方法返回元素的下标,与nextIndex相等。
public int previousIndex() {