继续分析List的整体框架结构:
从上图可以看出,LinkedList与ArrayList和Vector不同,它直接继承的父类是AbstractSequentialList而不是AbstractList。 我们再来看一看它的体系结构:
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>,Deque<E>,Cloneable,java.io.Serializable
从上图可以看出,LinkedList实现的接口除了ArrayList和Vector都实现过的List、Cloneable和Serializeable接口外,还实现了一个Deque接口,那来分析分析其父类AbstractSequentialList类和额外接口Deque到底有什么作用,为什么要继承这个父类和实现这个额外接口。
首先来分析一下AbstractSequentialList和AbstractList的区别,上源码。
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
public E set(int index, E element) {
throw new UnsupportedOperationException();
}
abstract public E get(int index);
简单截取一部分AbstractList的增删改查代码,发现四个方法都没有给出具体实现,如何实现都是让其子类或者子类的子类自己决定。
再来看AbstractSequentialList:
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 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);
}
}
public E get(int index) {
try {
return listIterator(index).next();
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
从上述源代码可以看出,AbstractSequentialList将增删改查都基本实现了一遍,而且都使用的是双向迭代器进行实现,也就是说继承了AbstractSequentialList这个父类,就要实现ListIterator这个接口,并且用迭代器来实现增删改查操作。从这里我们就能清楚了,以这种规定限制了继承AbstractSequentialList的子类只能是链表这样的数据结构了。因为顺序表也使用这种迭代的方式进行增删改查会极大的降低效率。
接下来再看一看新实现的Deque
public interface Deque<E> extends Queue<E> {
void addFirst(E e);
void addLast(E e);
boolean offerFirst(E e);
boolean offerLast(E e);
E removeFirst();
E removeLast();
E pollFirst();
E pollLast();
E getFirst();
E getLast();
E peekFirst();
E peekLast();
boolean removeFirstOccurrence(Object o);
boolean removeLastOccurrence(Object o);
boolean add(E e);
boolean offer(E e);
E remove();
E poll();
E element();
E peek();
void push(E e);
E pop();
boolean remove(Object o);
boolean contains(Object o);
public int size();
Iterator<E> iterator();
Iterator<E> descendingIterator();
}
从上面可以看出Deque是队列Queue的一个子接口,也被称为双向队列,是一个集大成者的数据结构,既可以实现先进后出模式的队列,也可以实现先进先出的栈,还可以实现顺序表中的某些方法,因此Deque的增删改查都对应着多种方法,分别用于不同的场景。所以实现了Deque接口的ArrayList也有着这么多的功能,既可以从头部插入或删除,也可以从尾部插入和删除。因此功能十分强大。
好了,框架结构基本梳理完了,接下来看LinkedList的成员变量:
transient int size = 0;
//设置链表的长度,不可序列化
transient Node<E> first;
//设置链表的第一个结点,不可序列化
transient Node<E> last;
//设置链表的最后一个结点,不可序列化
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;
}
}
LinkedList成员变量有三个,分别存储着链表的长度,链表的第一个结点和链表的最后一个结点,且三个成员变量都使用transient修饰,即表明该类对象序列化时不包括这些成员变量。此处还要介绍一下链表的底层存储结构Node,如上面代码所示,Node是LinkedList的私有静态内部类,其成员变量有三个,分别是本结点的内容item,下一个结点next,上一个结点prev,这两个也就相当于指针的,分别作为前置指针和后置指针,将链表中所有的结点联系起来。因此LinkedList的底层数据结构应该是双向链表。
再继续看一下LinkedList的构造器:
public LinkedList(){
//构造器1,空参构造器
}
public LinkedList(Collection<? extends E> c){
//构造器2,传入一个集合进行构造链表
this();
addAll(c);
}
LinkedList构造器只有两个,一个空参不做任何其他处理,另外一个传入一个集合,则首先调用空参构造器构造一个空的链表,再调用addAll方法将集合c插入到空链表中。因此链表最初始的时候应该是空的,当加入第一个元素进来时,它就创建一个结点将该元素存储下来,然后first和last都指向该元素,再从尾部添加一个元素时,链表再创建一个Node结点将元素保存,然后last指向新结点,从而形成了一个简单链表。
好,接下来上其余源码:
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>,Deque<E>,Cloneable,java.io.Serializable
{
transient int size = 0;
//设置链表的长度,不可序列化
transient Node<E> first;
//设置链表的第一个结点,不可序列化
transient Node<E> last;
//设置链表的最后一个结点,不可序列化
public LinkedList(){
//构造器1,空参构造器
}
public LinkedList(Collection<? extends E> c){
//构造器2,传入一个集合进行构造链表
this();
addAll(c);
}
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){
//在结点succ之前插入一个结点
final Node<E> pred = succ.prev;//获取succ之前的结点
final Node<E> newNode = new Node<>(pred, e, succ);//创建新结点
succ.prev = newNode;//将succ前置指针指向新结点
if(pred == null)//如果原前结点为空,说明succ为链表头部结点
first = newNode;//故将新结点设置为头结点
else
pred.next = newNode;//否则将原前结点的后置指针指向新结点
size++;//链表长度增加一
modCount++;
}
private E unlikFirst(Node<E> f){
//删除头结点
final E element = f.item;//获取头结点中的内容
final Node<E> next = f.next;//获取头结点后一个结点
f.item = null;//将头结点设置为空,方便回收
f.next = null;//将头结点的后置指针设置为空,方便回收
first = next;//将后一个结点设置为头结点
if(next == null)//如果后一个结点为空,说明此时链表为空,设置尾指针为空
last = null;
else
next.prev = null;//否则设置后一个结点的前置指针为空
size--;//链表长度减一
modCoun