可以看出来LinkedList不仅实现了List接口,还实现了Deque(双端队列),这使得LinkedList可以实现队列的一些特性
// 成员变量:header是一个entry实例,既然知道linkedlist是实现了链表结构,header
// 显然是头结点,而且可以看出来,header并不用于存放元素,而是为其他元素提供一个初始
// 位置
private transient Entry<E> header = new Entry<E>(null, null, null);
// size指当前链表中节点个数,不包括头结点
private transient int size = 0;
....
// entry静态内部类
private static class Entry<E> {
// 当前节点自身的内容
E element;
// 后一个节点的引用
Entry<E> next;
// 前一个节点的引用
Entry<E> previous;
// 第一个参数,第二个参数是next,第三个是previous,顺序要记住
Entry(E element, Entry<E> next, Entry<E> previous) {
this.element = element;
this.next = next;
this.previous = previous;
}
}
// 头结点的前一个节点指向自身,后一个节点也指向自身,由这个空链表也可以看出来LinkedList是一个双向循环
// 链表
public LinkedList() {
header.next = header.previous = header;
}
// 把集合转化成LinkedList
public LinkedList(Collection<? extends E> c) {
// 仍然首先定义一个空链表
this();
addAll(c);
}
public boolean addAll(Collection<? extends E> c) {
// 在size处插入集合元素
return addAll(size, c);
}
// index,从链表的index处(在index-1和index之间)开始插入给定的集合
public boolean addAll(int index, Collection<? extends E> c) {
// 如果index为负数或者index大于链表中节点个数,抛出异常
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index+ ", Size: "+size);
// 集合转数组,如果数组长度为0(集合元素为空),返回false
Object[] a = c.toArray();
int numNew = a.length;
if (numNew==0)
return false;
// 结构变化指标+1
modCount++;
// 如果index=size,就在header前插入,如果不是就在index对应的元素前插入
// successor是待插入的位置后的节点
Entry<E> successor = (index==size ? header : entry(index));
// 获得带插入位置的前一个节点,保存predecessor
Entry<E> predecessor = successor.previous;
// 遍历集合元素
for (int i=0; i<numNew; i++) {
// new一个新的entry,也就是带插入的节点,next为刚刚得到插入位置后的,previous是插入位置前的
// 这个new节点的步骤,实际上完成了插入节点本身前后两个属性引用的指向,也就是说到这个步骤为止
// 根据这个节点可以找到整个链表的元素,但是根据链表的其他节点还不能找到这个新插入的节点,因为和
// 它相邻的节点的next,previous的指向还没有改
Entry<E> e = new Entry<E>((E)a[i], successor, predecessor);
// 前一个节点的next指向新插入的节点,插入完成了2/3
predecessor.next = e;
// 把新插入的节点作为下一个带插入的节点的前一个节点,循环
predecessor = e;
}
// 循环结束,实际上这时候所有的集合元素,除了最后一个,都已经首尾相连,最后一个元素自身也已经指向原链表的
// index处的节点
// 插入工作的最后一步,index处的节点,previous引用指向集合的最后一个节点
successor.previous = predecessor;
// size加上集合长度
size += numNew;
return true;
}
// add方法,试图在linkedlist尾部插入一个元素
public boolean add(E e) {
addBefore(e, header);
return true;
}
=============================
// 在给定index之前插入一个元素
public void add(int index, E element) {
// 如果index=size,那么和直接add到链表末尾一样,调用addbefore插入到header之前
// 如果不是size,调用entry()返回带插入位置的当前节点
addBefore(element, (index==size ? header :entry(index)));
}
==============================
// 在给定参数entry节点前插入一个元素,对于header来说,header.previous
// 就是链表的最后一个,这也说明了linkedlist实现了一个双向循环链表
private Entry<E> addBefore(E e, Entry<E> entry) {
// 构成一个新的节点,previous是entry.previous,next是entry
// 对于header来说,新节点已经插入了last和header之间(仅其自身引用指向改变)
Entry<E> newEntry = new Entry<E>(e, entry, entry.previous);
// newEntry.previous就是新节点的前一个节点,但是它的next引用目前仍然
// 指向newEntry.next,所以修改之,下面同理。
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
// 节点数+1,结构变化+1,返回新插入的entry
size++;
modCount++;
return newEntry;
}
可以看出来add方法调用addbefore完成了在header之前,也就是链表末尾的插入工作。
get(int index)和addBefore(E e, Entry entry)都用到了entry(int index)方法,这个方法实际上是根据index对链表遍历返回对应的节点
public E get(int index) {
return entry(index).element;
}
private Entry<E> entry(int index) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
// header保存在局部变量e
Entry<E> e = header;
// 判断index在链表中的位置,跟size/2比较,决定是从前往后遍历还
// 是从后往前遍历
if (index < (size >> 1)) {
for (int i = 0; i <= index; i++)
e = e.next;
} else {
for (int i = size; i > index; i--)
e = e.previous;
}
return e;
}
// 从linkedlist中删除某个元素
public boolean remove(Object o) {
// 这里把非空作为分支条件主要是因为o.equals(),会空指针异常
if (o==null) {
// 从header往后遍历,如果为空(e.element==null),就删除该节点
for (Entry<E> e = header.next; e != header; e = e.next) {
if (e.element==null) {
remove(e);
return true;
}
}
} else {
for (Entry<E> e = header.next; e != header; e = e.next) {
// 如果element和给定的元素equals返回true,则删除该节点
if (o.equals(e.element)) {
remove(e);
return true;
}
}
}
return false;
}
===================================
private E remove(Entry<E> e) {
// header不能删除
if (e == header)
throw new NoSuchElementException();
// 把要删除的元素(不是节点),作为删除方法的返回值
E result = e.element;
// 修改其头尾节点的next和previous指向,跳过待删除的节点
e.previous.next = e.next;
e.next.previous = e.previous;
// 把删除的节点各个引用属性都改为null,等待gc回收
e.next = e.previous = null;
e.element = null;
//
size--;
modCount++;
return result;
}