本文通过JDK1.8版本LinkedList源码分(fan)析(yi)介绍LinkedList的底层实现方式和主要API,并介绍其与ArrayList的差异.
本文是该系列文的第四篇.
##1.顶部注释和LinkedList定义
/**
*双链列表是List和Deque接口的实现.其实现了所有list的可选择性操作,并且允许所有的元素(包括null,空元素)
*
*所有的操作是最大限度被期望成一个双链表形式的.在list中操作索引将从头或者尾在list中穿梭,这取决于哪一边跟靠近指定索引.
*
*注意LinkedList不是同步的,其线程是不安全的.当有多个线程同时操作LinkedList时,有可能会导致数据错误,
*所以如果需要多线程共享LinkedList时,最好使用synchronizedList来初始化:
* List list = Collections.synchronizedList(new LinkedList(...));
*
*大意为Collections.synchronizedList方法可以实现线程安全的操作。
*
*大意为由iterator()和listIterator()返回的迭代器是fail-fast的
*/
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
- LinkedList:说明它支持泛型。
- extends AbstractSequentialList
AbstractSequentialList 继承自AbstractList,但AbstractSequentialList - 只支持按次序访问,而不像 AbstractList 那样支持随机访问。这是LinkedList随机访问效率低的原因之一。
- implements List:说明它支持集合的一般操作。
- implements Deque:Deque,Double ended queue,双端队列。LinkedList可用作队列或双端队列就是因为实现了它。
为什么使用LinkedList?它的底层是如何保存数据的?
ArrayList的底层是数组实现,如果只在数组的高端执行插入(使用add),那么ArrayList非常适用.不过,如果不是在高端进行插入,那么ArrayList是一个极差的选择,因为我们必须移动很多项.
在LinkedList中,我们不是以常见的连续数组,而是以不连续的方式来存储项.为了做到这一点,我们将每个对象存储在一个节点中(node),节点包含对象和对表中下一节点的应用.在这种情况下,我们要维护表中对第一个节点和最后一个节点的引用.看看源码如何实现.
private static class Node<E> {//节点类,一个内部类
E item;//E类型的数据(域)
Node<E> next;//保存着下(后)一个的引用
Node<E> prev;//保存着上(前)一个的引用
Node(Node<E> prev, E element, Node<E> next) {//node的构造方法,直接传入(上一个节点引用,数据域,下一个节点的引用).
this.item = element;
this.next = next;
this.prev = prev;
}
}
##2.全局变量和构造方法
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
//LinkedLis大小,即节点个数
transient int size = 0;
/**
* 指向头节点
*/
transient Node<E> first;
/**
* 指向尾个节点
*/
transient Node<E> last;
/**
* 构造一个空的LinkedList
*/
public LinkedList() {
}
/**
*构造一个list,其包含着指定集合中的元素,按照此集合迭代器返回的顺序.
*
* @param c 将放入这个LinkedList的元素们所在的集合.
* @throws NullPointerException 如果指定的集合为空.
*/
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
##3 操作LinkedList底层Node的方法
底层操作包含2类(对节点的添加,删除)6个.
/**
* 链接e作为第一个元素(头结点)
*/
private void linkFirst(E e) {
// 使一个新的节点f指向原来的头结点first.
final Node<E> f = first;
/*
* 创建新的节点newNode,使新的节点的往前指的上一个节点引用为空 (即没有上一个节点,因为自己就是头结点),下一个节点的引用是原来的first
*/
final Node<E> newNode = new Node<>(null, e, f);
// 现在头指针不再指向原来的头结点,而指向newNode
first = newNode;
// 如果列表本来为空
if (f == null)
last = newNode;// 则尾元素也是新建的元素
else
// 原来的第一个节点(现在的第二个)头部指向新建的头结点
f.prev = newNode;
size++;//
modCount++;
}
/**
* 连接e作为其尾节点
*/
void linkLast(E e) {
// 将l指向原来的尾节点
final Node<E> l = last;
// 将newNode指向新的尾节点,这个新的尾节点向前指向last(即原来的尾节点),向后没有.
final Node<E> newNode = new Node<>(l, e, null);
// 原来的尾节点的引用指向了newNode
last = newNode;
if (l == null)
first = newNode;// 如果LinkedList为空的话,那头结点是它啦
else
// 当然,还需要node内部实现:原来尾节点的向后指针指向newNode
l.next = newNode;
size++;
modCount++;
}
/**
* 在指定的节点succ前插入一个元素e
*/
void linkBefore(E e, Node<E> succ) {
// 假设succ不为null
final Node<E> pred = succ.prev;//得到succ的前一个节点引用.
//构造一个新节点newNode,其向前的指针指向succ的前一个节点引用,向后的指向succ
final Node<E> newNode = new Node<>(pred, e, succ);
//现在succ的向前指针也指向了newNode了
succ.prev = newNode;
if (pred == null)//如果succ是头结点的话
first = newNode;
else
pred.next = newNode;//原来succ的前一个节点引用 的向后的指针将指向newNode
size++;
modCount++;
}
/**
* 删除头结点元素f,假设其不为null
*/
private E unlinkFirst(Node<E> f) {
// 假设 f == first && f != null;
final E element = f.item;//先把数据获取出来,用element指向
final Node<E> next = f.next;//获取头节点后面一个节点
f.item = null;//使头结点上的数据为空
f.next = null; // help GC
first = next;//原本的头结点的引用指向了next
if (next == null)//如果next也为空
last = null;//则尾节点也是空,说明移出后这个list里没元素了
else
next.prev = null;//next的向前指向也为空了,因为它自己就是头结点
size--;
modCount++;
return element;//返回删除的节点的数据
}
/**
* 移除尾节点l,假设其不为null
*/
private E unlinkLast(Node<E> l) {
// 假设 l == last && l != null;
final E element = l.item;//还是先把l的数据取出来,用引用element指向,等会返回
final Node<E> prev = l.prev;//获取l的前一个节点,用prev指向
l.item = null;//将l的数据设置为null
l.prev = null; // help GC
last = prev;//原来尾节点的引用指向了prev
if (prev == null)//如果prev为空
first = null;//则头结点也为空,说明移出后这个list里没有元素了
else
prev.next = null;//现在prev的下一个节点引用为空了
size--;
modCount++;
return element;
}
/**
* 移除指定的不为空的节点
*/
E unlink(Node<E> x) {
// 假设 x != null;
final E element = x.item;//用element保存x的数据
final Node<E> next = x.next;//next指向x的后一个节点
final Node<E> prev = x.prev;//prev指向x的前一个节点
if (prev == null) {//prev为空,即x是头结点
first = next;//x的后一个节点将成为list的头结点
} else {
prev.next = next;//x的前一个节点引用的向后指针将指向x的下一个,即将x跳过了
x.prev = null;
}
if (next == null) {//next为空,即x是尾节点
last = prev;
} else {
next.prev = prev;//x的后一个节点的向前指向的指针将指向x的前一个节点,巧妙地避开了x
x.next = null;
}
x.item = null;//x的数据也为null,至此,x为null了
size--;
modCount++;
return element;
}
##4.LinkedList的常用方法
####单个添加,删除,查找
/**
* 返回list中的第一个元素.
*/
public E getFirst() {
final Node<E> f = first;//得到头结点
if (f == null)
throw new NoSuchElementException();//如果为空,将报异常
return f.item;//注意,一般我们获取的是节点中保存的数据
}
/**
* 返回list中的最后一个元素
*/
public E getLast() {
final Node<E> l = last;//得到尾节点,l来指向
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);//底层调用的是unlinkFirst方法,就可以返回移除的头结点的数据了
}
/**
* 移除并返回最后一个元素
*/
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
/**
* 在list头添加指定的元素e
*/
public void addFirst(E e) {
linkFirst(e);//底层调用的是linkFirst(e)方法
}
/**
* 在表尾添加指定的元素.
*/
public void addLast(E e) {
linkLast(e);
}
####List通用操作(1)
前面很多已介绍,这里看看实现即可
public boolean contains(Object o) {
return indexOf(o) != -1;
}
public int size() {
return size;
}
/**
*在list尾端继续添加指定元素,添加成功返回true,该效果等同于addLast()方法,底层实现也是一样的
*/
public boolean add(E e) {
linkLast(e);
return true;
}
/**
*移除指定元素的在最低索引上的元素,如果其不存在与该list,则该list不变.如果list有指定元素并移除成功,返回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;
}
/**
*添加指定集合到list的尾端,其顺序即为迭代顺序
*/
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);//底层就是调用的接下来的方法
}
/**
*将指定的集合从指定的位置上开始插入到list中.后面的元素全部向后移位,链表发生改变,返回true
*/
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);//检查插入的位置是否合法
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;//如果c是空的话那么就返回false
//定义两个节点指针,指向插入点前后的节点元素
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
// 插入集合中所有元素
for (Object o : a) {
@SuppressWarnings("unchecked")
E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
// 修改插入后的指针问题
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
/**
* 删除list中的所有元素
*/
public void clear() {
// Clearing all of the links between nodes is "unnecessary", but:
// - helps a generational GC if the discarded nodes inhabit
// more than one generation
// - is sure to free memory even if there is a reachable Iterator
for (Node<E> x = first; x != null;) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}
####List的通用操作(2)
原文注释的是 “位置获取操作”
先来看一个方法,其为包访问权限.之后很多方法会用到.
/**
* 返回指定索引处的非空元素
*/
Node<E> node(int index) {
// 假设已经是合法索引
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
/**
* 根据索引得到元素
*/
public E get(int index) {
checkElementIndex(index);//检查查找的位置是否合法
return node(index).item;//node方法
}
/**
*将指定索引上的元素替换为指定的元素,返回被替换的元素(节点的数据)
*/
public E set(int index, E element) {
checkElementIndex(index);//检查传入的位置参数是否合法
Node<E> x = node(index);//通过位置获取节点
E oldVal = x.item;//得到该位置上的节点的数据,用oldVal指向
x.item = element;//将要传入的元素赋给x节点的数据
return oldVal;//oldVal
}
/**
* 在指定位置插入元素e
*/
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
/**
*移除指定位置上的元素,并将其返回
*/
public E remove(int index) {
checkElementIndex(index);//检查索引参数是否合法
return unlink(node(index));
}
/**
*检查索引是否越界
*/
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
private boolean isPositionIndex(int index) {
return index >= 0 && index <= size;
}
/**
* 创建一个越界信息,在服务器端和客户端虚拟机上都好用?
*/
private String outOfBoundsMsg(int index) {
return "Index: " + index + ", Size: " + size;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
####List的通用操作(3)
原文注释:查询操作
/**
*返回指定元素的最小索引,若list中没有,则返回-1
*/
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
/**
*返回指定元素的最大索引,若list中没有,则返回-1
*/
public int lastIndexOf(Object o) {
int index = size;
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
return -1;
}
##5.LinkedList的队列操作和双向队列操作
####(1)队列操作
/**
*返回头结点的元素,但是并不移除它
*如果头节点为null,也返回null
*/
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
/**
* 获取表头节点的值(第一个元素),头节点为空抛出异常
*/
public E element() {
return getFirst();
}
/**
*返回并删除头节点,如果该list为空则返回null
*/
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
/**
*移除并返回头节点,如果链表为空,抛出NoSuchElementException异常
*/
public E remove() {
return removeFirst();
}
/**
* 添加指定元素到list尾部
*/
public boolean offer(E e) {
return add(e);
}
####(2)双向队列操作
/**
* 将指定元素插入队头,插入成功返回true
*/
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
/**
* 将指定元素插入队伟,插入成功返回true
*/
public boolean offerLast(E e) {
addLast(e);
return true;
}
/**
* 返回,但不是移除,list头元素,若头结点为空,返回空
*/
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
/**
* 返回,但不是移除,list尾元素,若头结点为空,返回空
*/
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
/**
* 移除并且返回list的头元素,若节点为空,则返回null
*/
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
/**
*移除并且返回list的尾元素,若节点为空,则返回null
*/
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
/**
*将指定元素压入到栈头.换句话说,插入指定元素到list头
* 此方法等价于addFirst(e)
*/
public void push(E e) {
addFirst(e);
}
/**
* 移除并返回栈顶元素
*
* 此方法等价于removeFirst()
*/
public E pop() {
return removeFirst();
}
/**
*正向遍历栈,删除指定对象第一次出现时,索引对应的元素,若有这个元素,则返回true
*/
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}
/**
* 反向向遍历栈,删除指定对象第一次出现时,索引对应的元素,若有这个元素,则返回true
*/
public boolean removeLastOccurrence(Object o) {
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
##6.其余操作
/**
* 返回一个这个LinkedList的浅拷贝(这个元素本身并没有被克隆)
*/
public Object clone() {
LinkedList<E> clone = superClone();
// Put clone into "virgin" state
clone.first = clone.last = null;
clone.size = 0;
clone.modCount = 0;
// Initialize clone with our elements
for (Node<E> x = first; x != null; x = x.next)
clone.add(x.item);
return clone;
}
/**
* 将所有链表元素按顺序存到数组中,并返回这个数组
*/
public Object[] toArray() {
Object[] result = new Object[size];
int i = 0;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
}
/**
* 将所有list元素按顺序存到数组中。数组元素的类型是给定的类型。如果list元素的类型和数组元素类型不匹配,将新建一个数组来存储链表元素
*
* @throws NullPointerException 如果指定的数组为null
*/
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
int i = 0;
Object[] result = a;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
if (a.length > size)
a[size] = null;
return a;
}
##7.关于其内部类ListItr
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
private class ListItr implements ListIterator<E> {
private Node<E> lastReturned;
private Node<E> next;
private int nextIndex;
private int expectedModCount = modCount;
ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
}
public boolean hasNext() {
return nextIndex < size;
}
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
public boolean hasPrevious() {
return nextIndex > 0;
}
public E previous() {
checkForComodification();
if (!hasPrevious())
throw new NoSuchElementException();
lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
}
public int nextIndex() {
return nextIndex;
}
public int previousIndex() {
return nextIndex - 1;
}
public void remove() {
checkForComodification();
if (lastReturned == null)
throw new IllegalStateException();
Node<E> lastNext = lastReturned.next;
unlink(lastReturned);
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
}
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e;
}
public void add(E e) {
checkForComodification();
lastReturned = null;
if (next == null)
linkLast(e);
else
linkBefore(e, next);
nextIndex++;
expectedModCount++;
}
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (modCount == expectedModCount && nextIndex < size) {
action.accept(next.item);
lastReturned = next;
next = next.next;
nextIndex++;
}
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
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的序列化,JDK8新特性等并没有将源码贴出,这一部分相对用的较少,之后慢慢加吧.
最最后,我们将ArrayList和LinkedList做一个比较吧
- ArrayList底层由数组实现,LinkedList底层是双向链表.
- 二者元素都为有序且可重复.(毕竟实现List接口).
- ArrayList的查改(随机访问)效率高,增删除效率低.LinkedList随机访问效率低,增删效率高。
感谢大神们的文章,获益匪浅,本文有参考自:
潘威威的博客— Java8源码-LinkedList