书接上文,上一篇中对链表结构最终实现类 Vector 做了分析,本篇将继续对 LinkedList 做分析。
LinkedList 与 ArrayList/Vector 有一点不同,它的继承关系是 AbstractList -》 AbstractSequentialList -〉 LinkedList,并且实现了 Deque 接口,而 ArrayList/Vector 是直接继承自 AbstractList 的,首先来看下 LinkedList 的关系图
所以在查看 LinkedList 之前,还需要看一下 Deque(与 Queue) 的接口
Queue
/**
Qeueu 接口,一种被设计用于存放加工前数据的数据结构。除了基础的 Collection 方法外,队列提供了额外的添加,提取和检查操作。每个这样的操作都有两种形式:一种在操作失败时抛出一个异常,另一种返回一个特殊值(可能是 null 或者 false,具体看操作)。添加操作的后裔中方式被特别设计用来配合优先容量的 Queue 实现类实现,大部分的 Queue 的是下了,插入操作是不会失败的。
继承自 Collection 接口
**/
public interface Queue<E> extends Collection<E> {
/**向队列中插入指定参数,如果它能不考虑容量限制,就能快速地避开违反容量限制(翻译的可能不太准确。。。),如果成功返回 true,如果当前没有剩余空间了则抛出 IllegalStateException**/
boolean add(E e);
/**向队列中插入指定参数,如果它能不考虑容量限制,就能快速地避开违反容量限制,当使用一个容量有限的队列时,建议使用 add 方法,它(add)是一个只能在抛出异常时插入失败的方法**/
boolean offer(E e);
/**获取并且移除队列的头部元素。这个方法与 poll 方法唯一的区别是它会在队列为空时抛出一个异常**/
E remove();
/**获取并且移除队列的头部元素。当队列为空时返回 null**/
E poll();
/**返回队列的头部元素,但不移除。这个方法和 peek 方法唯一的区别是它会在队列为空时抛出一个异常**/
E element();
/**返回队列的头部元素,但不移除。当队列为空时返回 null**/
E peek();
}
可以看到 Queue 继承自 Collection 接口,并提供一些针对队列的操作方式。每一种方式都有成对的方法,一种返回异常,一种返回 false 或者 null。
Deque
/**
Deque 接口,一种支持在双端进行元素插入和移除的线性数据结构。 “deque” 是 “double ended queue”(双端队列)的缩写。大部分 Deque 的实现类没有固定的元素上限,不会这个接口同时支持有上限的 deque 与无上限的。
这个接口定义了从 deque 双端访问元素的方法。提供的方法有插入,移除和校验元素。每个方法都有两种形式:一种在操作失败时抛出异常,另一种返回一个特殊的值(可能是 null 或者 false,具体要看操作)。插入操作的后面一种形式被特别设计来配合有限容量 Deque 实现类使用,在大部分实现类中,插入操作不会失败。
继承自 Queue 接口
**/
public interface Deque<E> extends Queue<E> {
/**向双端队列中的开头插入指定参数,如果它能不考虑容量限制,就能快速地避开违反容量限制,当没有剩余空间时抛出一个 IllegalStateException。当使用一个容量有限的双端队列时,建议使用 offerFirst 方法**/
void addFirst(E e);
/**向双端队列中的末尾插入指定参数,如果它能不考虑容量限制,就能快速地避开违反容量限制,当没有剩余空间时抛出一个 IllegalStateException。当使用一个容量有限的双端队列时,建议使用 offerLast 方法**/
void addLast(E e);
/**向双端队列中的开头插入指定参数,除非它会违反容量限制。当使用一个容量有限的双端队列时,建议使用 addFirst 方法,它(addFirst)只在能抛出一个异常时插入失败**/
boolean offerFirst(E e);
/**向双端队列中的末尾插入指定参数,除非它会违反容量限制,当使用一个容量有限的双端队列时,建议使用 addLast 方法,它(addlast)只能在抛出一个异常时插入失败**/
boolean offerLast(E e);
/**获取并移除双端队列的第一个元素,这个方法和 pollFirst 方法的唯一区别在于它会在双端队列中没有元素时抛出一个异常**/
E removeFirst();
/**获取并移除双端队列最后一个元素,这个方法和 pollLast 方法的唯一区别在于它会在双端队列中没有元素时抛出一个异常**/
E removeLast();
/**获取并移除双端队列的第一个元素,当双端队列中没有元素时返回 null**/
E pollFirst();
/**获取并移除双端队列最后一个元素,当双端队列中没有元素时返回 null**/
E pollLast();
/**返回双端队列的第一个元素,但是不移除,这个方法和 peekFirst 方法的唯一区别在于它会在双端队列中没有元素时抛出一个异常**/
E getFirst();
/**返回双端队列的第一个元素,但是不移除,这个方法和 peekLast 方法的唯一区别在于它会在双端队列中没有元素时抛出一个异常**/
E getLast();
/**返回双端队列的第一个元素,但是不移除,或者当双端队列中没有元素时返回 null**/
E peekFirst();
/**返回双端队列的最后个元素,但是不移除,或者当双端队列中没有元素时返回 null**/
E peekLast();
/**移除双端队列中的第一个与指定参数相同的元素。如果双端队列不包含这种元素,它将保持不变。**/
boolean removeFirstOccurrence(Object o);
/**移除双端队列中最后一个与指定参数相同的元素。如果双端队列不包含这种元素,它将保持不变。**/
boolean removeLastOccurrence(Object o);
// 查询操作
/**在这个双端队列提供的队列中插入指定元素(换一种说法,在双端队列的末尾)如果它能不考虑容量限制,就能快速地避开违反容量限制,操作成功时返回 true,如果当前没有剩余空间时抛出一个 IllegalStateException**/
boolean add(E e);
/**在这个双端队列提供的队列中插入指定元素(换一种说法,在双端队列的末尾)如果它能不考虑容量限制,就能快速地避开违反容量限制,操作成功时返回 true,如果当前没有剩余空间时返回 false。当使用容量有限的双端队列时,建议使用 add 方法,它(add)只有在抛出异常时才插入失败**/
boolean offer(E e);
/**获取并移除当前双端队列提供的队列的头部元素(换句话说,双端队列中的第一个元素)。这个方法与 poll 方法唯一的区别是它会在队列中没有元素时抛出一个异常**/
E remove();
/**获取并移除当前双端队列提供的队列的头部元素(换句话说,双端队列中的第一个元素)。当双端队列中没有元素时候返回 null**/
E poll();
/**获取当前双端队列提供的队列的头部元素(换句话说,双端队列中的第一个元素),但不移除。这个方法和 peek 方法唯一的区别是它会在双端队列中没有元素时抛出一个异常**/
E element();
/**获取当前双端队列提供的队列的头部元素(换句话说,双端队列中的第一个元素),但不移除。当双端队列中没有元素时返回 null**/
E peek();
// 栈方法
/**向当前双端队列提供的队列的栈(换句话说,双端队列的头部)压入一个元素,如果它能不考虑容量限制,就能快速地避开违反容量限制,当没有剩余空间时抛出一个 IllegalStateException**/
void push(E e);
/**从当前双端队列提供的栈弹出一个元素。换句话说,移除并返回双端队列的第一个元素**/
E pop();
// 集合方法
/**移除双端队列中第一个与指定参数相同的元素。如果双端队列中不包含这样的元素,则保持不变**/
boolean remove(Object o);
/**判断当前双端队列是否包含与指定参数相同的元素,如果包含,返回 true**/
boolean contains(Object o);
/**返回这个双端队列的元素数量**/
public int size();
/**以正确的顺序返回双端队列的一个串行迭代器**/
Iterator<E> iterator();
/**按反向顺序返回双端队列的一个串行迭代器**/
Iterator<E> descendingIterator();
}
从 Deque 的源码可以看到,Deque 扩展了 Queue 接口的方法,使得 Queue 中的队列操作可以既可以从头也可以从尾部进行,并提供了栈相关方法的定义与获取反向迭代器方法。
然后来认识一下 AbstractSequentialList
AbstractSequentialList
/**
本类提供一个 List 接口的实现框架来最小化它的支持“顺序访问”数据存储(比如关联)的实现类所需要的实现。如果要随机访问数据(比如一个数组),应该先考虑 AbstractList 类而不是本类。继承自 AbstractList 类。
**/
public abstract class AbstractSequentialList<E> extends AbstractList<E> {
/**唯一的构造方法,通常是隐式的被子类构造方法调用**/
protected AbstractSequentialList() {
}
/**返回链表中 int 参数指定的位置的元素**/
public E get(int index) {
try {
return listIterator(index).next(); //首先返回一个带 index 参数的串行迭代器,然后返回迭代器中的下一个元素
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
/**在 index 指定位置替换 e 参数(可选操作)**/
public E set(int index, E element) {
try {
ListIterator<E> e = listIterator(index); //获得链表的带参串行迭代器
E oldVal = e.next(); //缓存下一个元素
e.set(element); //设置元素
return oldVal; //返回缓存的旧值
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
/**在 index 指定位置添加 e 参数(可选操作),移动当前和其后的元素至右边(下标 + 1)**/
public void add(int index, E element) {
try {
listIterator(index).add(element); //先获取带参迭代器,然后调用迭代器的添加方法
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
/**移除链表中指定位置的元素(可选操作),左移其后的元素(通过递减它们的下标)。返回被移除的元素**/
public E remove(int index) {
try {
ListIterator<E> e = listIterator(index); //先获取带参迭代器
E outCast = e.next(); //获得下一个元素并缓存
e.remove(); //移除
return outCast; //返回缓存的元素
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
//扩展
/**在指定参数位置添加指定容器参数中的所有元素(可选操作),右移原位置元素与其后的元素(增加下标)。新的元素的顺序与它们在迭代器中的顺序相同。如果容器参数在过程中更改了,那么这个操作的行为是无法定义的。(注意当容器参数是自己并且它非空时候会发生。这句话不是很明白。。)**/
public boolean addAll(int index, Collection<? extends E> c) {
try {
boolean modified = false; //初始化修改标志
ListIterator<E> e1 = listIterator(index); //获取迭代器
Iterator<? extends E> e2 = c.iterator(); //后去容器参数迭代器
while (e2.hasNext()) {
//遍历判断游标后是否还有元素
e1.add(e2.next()); //获取后一个元素,加入 e1
modified = true; //修改标志赋值为 true
}
return modified; //返回修改标志
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
// 迭代器
/**返回一个包含正确排序的元素的串行迭代器**/
public Iterator<E> iterator() {
return listIterator();
}
/**返回一个带参链表串行迭代器**/
public abstract ListIterator<E> listIterator(int index);
}
由 AbstractSequentialList 的源码可以看到,它实现了几个在 AbstractList 中定义了但是如果自己不重写就会抛出异常的方法,它们的实现方式都是需要先获得迭代器,所以可以认为 AbstractSequentialList 类的所有方法都是针对迭代器的。
现在可以来看 LinkedList 的源码了
/**
双链接链表实现了 List 和 Deque 接口。实现了所有可选链表操作,并且允许所有元素(包括 null)。
所以的操作作为可以表现为符合一个双链接链表的期待。链表中的位置操作将从开始到结束贯穿整个链表,知道靠近参数指定位置。
注意这个实现类不是线程安全的。如果读线程同时访问一个链接链表,并且至少有一个线程建构性的改变了链表,它必须从外部保证线程安全。一个结构性的修改是指任何添加或者删除一个或多个元素的操作,或者明切重定义了支持数组的大小,仅仅为一个元素设值不是一个结构性的修改。)这通常是通过使某些封装了链表的对象实现线程安全来做到的。
如果没有类似的对象存在,链表应该被使用 Collection。sychronizedList 方法进行“包装”。这最好在创建时完成,来防止意外的对链表的非线程安全的访问。比如:
List list = Collections.synchronizedList(new LinkedList(...));
通过 iterator 方法或者 listIterator 返回的迭代器是 fail-fast 的,如果在迭代器创建后的任意时间点链表被结构性的改变了,只要不是通过迭代器自己的 ListIterator#remove 或者 ListIterator#add 方法,迭代器将会抛出一个同时修改异常,迭代器将快速干净的失败,
注意一个迭代器的 fail-fast 行为是不能被保证的,通常来说,不可能对出现的非线程安全的同时修改操作做任何硬性的保证。基于最佳性能的基础考虑,Fail-fast 迭代器抛出一个 ConcurrentModificationException。因此,建立在这中异常上写出的程序的正确性将会是不健壮的:迭代器的 fail-fast 行为应当植被用于检查 bugs。
**/
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
/**长度,不被序列化**/
transient int size = 0;
/**首节点
* 永恒不变的:首节点和尾节点都为 null 或者首节点的前节点为 null 并且首节点内容不为 null**/
transient Node<E> first;
/**尾节点
* 永恒不变的:首节点和尾节点都为 null 或者尾节点指针的后节点为 null 并且尾节点内容不为 null**/
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; //首节点缓存给 f
final Node<E> newNode = new Node<>(null, e, f); //调用 Node 构造器,传入参数与首节点,构造一个新的节点
first = newNode; //新节点赋值给首节点
if (f == null) //如果 f 为 null,说明原来没有首节点
last = newNode; //新节点赋值给尾节点
else //如果 f 不为 null,说明原来有首节点
f.prev = newNode; //新节点赋值给 f 的前节点
size++; //更新长度
modCount++; //更新 modCount
}
/**将制定参数 E 链接为尾节点**/
void linkLast(E e) {
final Node<E> l = last; //缓存尾节点给 l
final Node