概要
前面,我们已经学习了ArrayList,并了解了fail-fast机制。接着学习List的实现类——LinkedList。
和学习ArrayList一样,接下来呢,我们先对LinkedList有个整体认识,然后再学习它的源码;最后再通过实例来学会使用LinkedList。内容包括:
1、LinkedList介绍
LinkedList简介
LinkedList是一个继承AbstactSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
LinkedList实现List接口,能对它进行队列操作。
LinkedList实现Deque接口,即能将LinkedList当成双端队列来使用。
LinkedList实现java.io.Serializable接口,这意味着LinkedList支持序列化,能通过序列化去传输。
LinkedList是非同步的。
构造函数
Constructor and Description
LinkedList()
构造一个空列表。
LinkedList(Collection<? extends E> c)
构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
LinkedList的API
Modifier and Type Method and Description
boolean add(E e)
将指定的元素追加到此列表的末尾。
void add(int index, E element)
在此列表中的指定位置插入指定的元素。
boolean addAll(Collection<? extends E> c)
按照指定集合的迭代器返回的顺序将指定集合中的所有元素追加到此列表的末尾。
boolean addAll(int index, Collection<? extends E> c)
将指定集合中的所有元素插入到此列表中,从指定的位置开始。
void addFirst(E e)
在该列表开头插入指定的元素。
void addLast(E e)
将指定的元素追加到此列表的末尾。
void clear()
从列表中删除所有元素。
Object clone()
返回此 LinkedList的浅版本。
boolean contains(Object o)
如果此列表包含指定的元素,则返回 true 。
Iterator<E> descendingIterator()
以相反的顺序返回此deque中的元素的迭代器。
E element()
检索但不删除此列表的头(第一个元素)。
E get(int index)
返回此列表中指定位置的元素。
E getFirst()
返回此列表中的第一个元素。
E getLast()
返回此列表中的最后一个元素。
int indexOf(Object o)
返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
int lastIndexOf(Object o)
返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
ListIterator<E> listIterator(int index)
从列表中的指定位置开始,返回此列表中元素的列表迭代器(按适当的顺序)。
boolean offer(E e)
将指定的元素添加为此列表的尾部(最后一个元素)。
boolean offerFirst(E e)
在此列表的前面插入指定的元素。
boolean offerLast(E e)
在该列表的末尾插入指定的元素。
E peek()
检索但不删除此列表的头(第一个元素)。
E peekFirst()
检索但不删除此列表的第一个元素,如果此列表为空,则返回 null 。
E peekLast()
检索但不删除此列表的最后一个元素,如果此列表为空,则返回 null 。
E poll()
检索并删除此列表的头(第一个元素)。
E pollFirst()
检索并删除此列表的第一个元素,如果此列表为空,则返回 null 。
E pollLast()
检索并删除此列表的最后一个元素,如果此列表为空,则返回 null 。
E pop()
从此列表表示的堆栈中弹出一个元素。
void push(E e)
将元素推送到由此列表表示的堆栈上。
E remove()
检索并删除此列表的头(第一个元素)。
E remove(int index)
删除该列表中指定位置的元素。
boolean remove(Object o)
从列表中删除指定元素的第一个出现(如果存在)。
E removeFirst()
从此列表中删除并返回第一个元素。
boolean removeFirstOccurrence(Object o)
删除此列表中指定元素的第一个出现(从头到尾遍历列表时)。
E removeLast()
从此列表中删除并返回最后一个元素。
boolean removeLastOccurrence(Object o)
删除此列表中指定元素的最后一次出现(从头到尾遍历列表时)。
E set(int index, E element)
用指定的元素替换此列表中指定位置的元素。
int size()
返回此列表中的元素数。
Spliterator<E> spliterator()
在此列表中的元素上创建late-binding和故障快速 Spliterator 。
Object[] toArray()
以正确的顺序(从第一个到最后一个元素)返回一个包含此列表中所有元素的数组。
<T> T[] toArray(T[] a)
以正确的顺序返回一个包含此列表中所有元素的数组(从第一个到最后一个元素);
返回的数组的运行时类型是指定数组的运行时类型。
AbstractSequentialList简介
在介绍LinkedList的源码之前,先介绍一下AbstractSequentialList。毕竟,LinkedList是AbstractSequentialList的子类。
AbstractSequentialList 实现了get(int index)、set(int index, E element)、add(int index, E element) 和 remove(int index)这些函数。这些接口都是随机访问List的,LinkedList是双向链表;既然它继承于AbstractSequentialList,就相当于已经实现了“get(int index)这些接口”。
此外,我们若需要通过AbstractSequentialList自己实现一个列表,只需要扩展此类,并提供 listIterator() 和 size() 方法的实现即可。若要实现不可修改的列表,则需要实现列表迭代器的 hasNext、next、hasPrevious、previous 和 index 方法即可。
2、LinkedList数据结构
LinkedList的继承关系
java.lang.Object
- java.util.AbstractCollection<E>
- java.util.AbstractList<E>
- java.util.AbstractSequentialList<E>
- java.util.LinkedList<E>
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, Serializable
LinkedList与Collection关系如下图:
LinkedList的本质是双向链表。
(01) LinkedList继承于AbstractSequentialList,并且实现了Dequeue接口。
(02) LinkedList包含两个重要的成员:header 和 size。
header:是双向链表的表头,它是双向链表节点所对应的类Entry的实例。Entry中包含成员变量:previous,next,element。其中,previous是该节点的上一个节点,next是该节点的下一个节点,element是该节点所包含的值。
size:是双向链表中节点的个数。
3、LinkedList源码解析(JDK1.8)
为了更了解LinkedList的原理,下面对LinkedList源码代码作出分析。
在阅读源码之前,我们先对LinkedList的整体实现进行大致说明:
LinkedList实际上是通过双向链表去实现的。既然是双向链表,那么它的顺序访问会非常高效,而随机访问效率比较低。
既然LinkedList是通过双向链表的,但是它也实现了List接口{也就是说,它实现了get(int location)、remove(int location)等“根据索引值来获取、删除节点的函数”}。LinkedList是如何实现List的这些接口的,如何将“双向链表和索引值联系起来的”?
实际原理非常简单,它就是通过一个计数索引值来实现的。例如,当我们调用get(int location)时,首先会比较“location”和“双向链表长度的1/2”;若前者大,则从链表头开始往后查找,直到location位置;否则,从链表末尾开始先前查找,直到location位置。
这就是“双线链表和索引值联系起来”的方法。
好了,接下来开始阅读源码(只要理解双向链表,那么LinkedList的源码很容易理解的)。
package java.util;
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
// 链表的表头,表头不包含任何数据。Entry是个链表类数据结构。
private transient Entry<E> header = new Entry<E>(null, null, null);
// LinkedList中元素个数
private transient int size = 0;
// 默认构造函数:创建一个空的链表
public LinkedList() {
header.next = header.previous = header;
}
// 包含“集合”的构造函数:创建一个包含“集合”的LinkedList
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
// 获取LinkedList的第一个元素
public E getFirst() {
if (size==0)
throw new NoSuchElementException();
// 链表的表头header中不包含数据。
// 这里返回header所指下一个节点所包含的数据。
return header.next.element;
}
// 获取LinkedList的最后一个元素
public E getLast() {
if (size==0)
throw new NoSuchElementException();
// 由于LinkedList是双向链表;而表头header不包含数据。
// 因而,这里返回表头header的前一个节点所包含的数据。
return header.previous.element;
}
// 删除LinkedList的第一个元素
public E removeFirst() {
return remove(header.next);
}
// 删除LinkedList的最后一个元素
public E removeLast() {
return remove(header.previous);
}
// 将元素添加到LinkedList的起始位置
public void addFirst(E e) {
addBefore(e, header.next);
}
// 将元素添加到LinkedList的结束位置
public void addLast(E e) {
addBefore(e, header);
}
// 判断LinkedList是否包含元素(o)
public boolean contains(Object o) {
return indexOf(o) != -1;
}
// 返回LinkedList的大小
public int size() {
return size;
}
// 将元素(E)添加到LinkedList中
public boolean add(E e) {
// 将节点(节点数据是e)添加到表头(header)之前。
// 即,将节点添加到双向链表的末端。
addBefore(e, header);
return true;
}
// 从LinkedList中删除元素(o)
// 从链表开始查找,如存在元素(o)则删除该元素并返回true;
// 否则,返回false。
public boolean remove(Object o) {
if (o==null) {
// 若o为null的删除情况
for (Entry<E> e = header.next; e != header; e = e.next) {
if (e.element==null) {
remove(e);
return true;
}
}
} else {
// 若o不为null的删除情况
for (Entry<E> e = header.next; e != header; e = e.next) {
if (o.equals(e.element)) {
remove(e);
return true;
}
}
}
return false;
}