LinkedList 与 ArrayList 同样实现了 List 接口,但它基于双向链表实现,插入和删除操作效率较高,而随机访问效率较低。本文通过源码来分析一下 LinkedList 的实现原理,注意事项,使用场景等,以便能更好地使用它(JDK 版本为 1.8)。
LinkedList 的主要特点如下:
LinkedList是List接口和Deque接口的双向链表实现;LinkedList实现了列表的所有操作,允许添加null;LinkedList不是同步的;- 由
iterator()和listIterator()返回的迭代器是fail-fast的。
定义
先来看一下 LinkedList 的定义:
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
可以看到 LinkedList 继承或实现了以下类或接口:
AbstractSequentialList:AbstractSequentialList继承自AbstractList,但AbstractSequentialList只支持按次序访问,而不像AbstractList那样支持随机访问。List::实现了List接口,提供了所有可选列表操作。Deque:代表双端队列,这是LinkedList可用作队列或双端队列的原因。Cloneable:表明其可以被克隆,重写了clone方法。java.io.Serializable:表明该类是可以序列化的。
但 LinkedList 没有实现 RandomAccess,说明 LinkedList 不支持随机访问,这就是 LinkedList 随机访问效率低的原因之一。
属性
LinkedList 的属性主要有:
// 节点个数
transient int size = 0;
// 指向头节点的指针
transient Node<E> first;
// 指向尾节点的指针
transient Node<E> last;
LinkedList 的内部类 Node 表示链表中的节点,包括一个数据域 item,一个后置指针 next,一个前置指针 prev。
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 中提供了两种构造方法:
构造空链表
public LinkedList() {
}
使用给定 collection 构造链表
构造方法中,先构造一个空链表,再把指定集合 collection 中的所有元素都添加到 LinkedList 中。
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
操作链表的底层方法
下面是几个操作链表的底层方法:
linkFirst 方法
该方法用于在链表头添加元素 e。
private void linkFirst(E e) {
// 使节点 f 指向原来的头节点
final Node<E> f = first;
// 新建节点 Node,前驱指针指向 null,后置指针指向原来的头节点
final Node<E> newNode = new Node<>(null, e, f);
// 头指针 first 指向新的头节点 newNode
first = newNode;
if (f == null)
// 如果原来的头节点为 null,则更新尾指针
last = newNode;
else
// 否则使原来的头节点 f 的前驱指针指向新的头节点 newNode
f.prev = newNode;
size++;
modCount++;
}
linkLast 方法
该方法用于在链表尾部添加元素 e。
void linkLast(E e) {
// 使节点 l 指向原来的尾节点
final Node<E> l = last;
// 新建节点 Node,前驱指针指向 l,后置指针指向 null
final Node<E> newNode = new Node<>(l, e, null);
// 尾指针 last 指向新的尾节点 newNode
last = newNode;
if (l == null)
// 如果原来的头节点为 null,则更新头指针
first = newNode;
else
// 否则使原来的尾节点 l 的后置指针指向新的尾节点 newNode
l.next = newNode;
size++;
modCount++;
}
linkBefore 方法
该方法用于在指定节点 succ 之前添加元素 e。
void linkBefore(E e, Node<E> succ) {
// 获得指定节点 succ 的前驱节点
final Node<E> pred = succ.prev;
// 新建节点 newNode,前置指针指向 pred,后置指针指向 succ
final Node<E> newNode = new Node<>(pred, e, succ);
// succ 的前置指针指向 newNode
succ.prev = newNode;
if (pred == null)
// 如果指定节点的前驱节点为 null,则将 newNode 置为头节点
first = newNode;
else
// 否则更新 pred 的后置节点
pred.next = newNode;
size++;
modCount++;
}
unlinkFirst 方法
该方法用于删除头节点,并返回头节点的值。
private E unlinkFirst(Node<E> f) {
// 保存头节点的值
final E element = f.item;
// 保存头节点的下一个节点
final Node<E> next = f.next;
// 头节点的值置为 null
f.item = null;
// 头节点的后置指针置为 null
f.next = null;
// 将头节点置为 next
first = next;
if (next == null)
// 如果 next 为 null,将尾节点置为 null
last = null;
else
// 否则将 next 的前驱指针指向 null
next.prev = null;
size--;
modCount++;
return element;
}
unlinkLast 方法
该方法用于删除尾节点,并返回尾节点的值。
private E unlinkLast(Node<E> l) {
// 保存尾节点的值
final E element = l.item;
// 保存尾节点的前一个节点
final Node<E> prev = l.prev;
// 尾节点的值置为 null
l.item = null;
// 尾节点的前驱指针指向 null
l.prev = null; // help GC
// 将尾节点置为 prev
last = prev;
if (prev == null)
// 如果 prev 为 null,将头节点置为 null
first = null;
else
// 否则将 prev 的后置指针指向 null
prev.next = null;
size--;
modCount++;
return element;
}
unlink 方法
该方法用于删除指定的节点 x。
E unlink(Node<E> x) {
// 保存指定节点的值、前驱节点、后置节点
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
if (prev == null) {
// 如果前驱节点为 null,表示删除的是头节点,则将 first 指向为 next
first = next;
} else {
// 否则将 prev 的后置指针指向 next,x 的前置指针指向 null
prev.next = next;
x.prev = null;
}
if (next == null) {
// 如果后置节点为 null,表示删除的是尾节点,则将 last 指向 prev
last = prev;
} else {
// 否则将 next 的前置指针指向 prev,x 的后置指针指向 null
next.prev = prev;
x.next = null;
}
// x 的值置为 null
x.item = null;
size--;
modCount++;
return element;
}
基本链表方法
add(E) 方法
add 方法在链表的末尾添加指定的元素 e。
public boolean add(E e) {
linkLast(e);
return true;
}
remove(Object) 方法
remove 方法用于在删除链表中出现的第一个指定的元素 o。
public boolean remove(Object o) {
if (o == null) {
// 如果 o 为 null,遍历链表,删除第一个值为 null 的节点,返回 true
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
// 否则删除第一个值为 o 的节点。如果链表中存在 o,就返回 true。
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
addAll(int, Collection<? extends E>) 方法
addAll 方法将给定的 collection 集合插入到从 index 位置开始的 List 中。
public boolean addAll(int index, Collection<? extends E> c) {
// 检查插入到位置是否合法
checkPositionIndex(index);
// 将 c 转换为数组
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0) // 如果 c 为空,那么就返回 false
return false;
// 使 pred 指向插入点前面的节点,succ 指向插入点后面(pred 的下一个)节点
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;
}
// 返回索引为 index 位置的节点
Node<E> node(int index) {
if (index < (size >> 1)) {
// 如果 index 小于链表的一半,则从表头开始遍历
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;
}
}
addAll 方法在链表中间插入元素原理图如下:
add(int, E) 方法
此 add 方法用于在 List 中索引为 index 位置插入元素 element。
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
// 如果 index 等于 size,则插入到链表尾
linkLast(element);
else
// 否则,插入到索引为 index 位置之前
linkBefore(element, node(index));
}
set(int, E) 方法
set 方法用给定的元素 element 代替 List 中索引为 index 位置的元素。
public E set(int index, E element) {
checkElementIndex(index);
// 返回索引为 index 位置的节点
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
get(int) 方法
get 方法会返回 List 中指定位置 index 的元素。
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
indexOf(Ojbect) 方法
index 方法返回 List 中指定元素第一次出现的下标。
public int indexOf(Object o) {
int index = 0;
if (o == null) {
// 如果 o 为 null,遍历链表,查找第一个为 null 的元素,返回 index
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
// 否则查找第一个为 Object 的元素,返回 index
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
getFirst/getLast 方法
getFirst 方法返回链表中第一个元素,而 getLast 方法返回链表中最后一个元素。
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
removeFirst/removeLast 方法
removeFirst 方法从 list 中删除第一个元素,并返回它;而 removeLast 方法从 list 中删除最后一个元素,并返回它。
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
addFirst/addLast 方法
addFirst 方法在链表头插入指定元素;addLast 方法在链表尾插入指定元素。
public void addFirst(E e) {
linkFirst(e);
}
public void addLast(E e) {
linkLast(e);
}
队列方法
peek/element 方法
peek/element 方法都是返回队列的队首元素。但是,如果队列为空,peek 方法返回 null,而 element 方法会抛出 NoSuchElementException。
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
public E element() {
return getFirst();
}
poll/remove 方法
poll/remvoe 方法都是删除队列的队首元素,并返回。但是如果队列为空,poll 方法会返回空,而 remove 方法会抛出 NoSuchElementException 异常。
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
public E remove() {
return removeFirst();
}
offer 方法
offer 方法在队列的队尾处添加指定元素。
public boolean offer(E e) {
return add(e);
}
双端队列方法
offerFirst/offerLast 方法
offerFirst 方法在队列的队首添加元素;而 offerLast 方法在队列的队尾添加元素。
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
public boolean offerLast(E e) {
addLast(e);
return true;
}
peekFirst/peekLast 方法
peekFirst 方法会返回队列的队首元素,但不删除。如果队列为空,会返回 null。
peekLast 方法会返回队列的队尾元素,但不删除。如果队列为空,会返回 null。
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
pollFirst/pollLast 方法
pollFirst 方法会删除队列的队首元素,并返回。如果队列为空,则返回 null。
pollLast 方法会删除队列的队尾元素,并返回。如果队列为空,则返回 null。
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
栈方法
push 方法将指定元素压入到栈顶。
public void push(E e) {
addFirst(e);
}
pop 方法从栈顶弹出元素。
public E pop() {
return removeFirst();
}