JDK 8 LinkedList 详解及详细源码展示
JDK 8 的 LinkedList
是 Java 集合框架中的核心实现,基于 双向链表 实现,支持高效的插入、删除操作,并提供了双向迭代器。本文结合源码剖析其设计哲学、核心实现及性能优化技术。
一、核心设计目标:双向链表与高效插入/删除
1.1 双向链表特性
- 双向指针:每个节点包含前驱(
prev
)和后继(next
)指针,支持双向遍历。 - 动态增长:无需预先分配固定容量,内存按需分配。
1.2 高效操作
- 插入/删除:时间复杂度为 O(1)(已知插入位置的前驱节点)。
- 随机访问:时间复杂度为 O(n),需从头或尾遍历。
二、源码核心类结构
// java.util.LinkedList.java
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable {
// 元素数量
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;
}
}
// 构造方法
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
}
三、核心方法源码解析
3.1 add
方法(尾部插入)
public boolean add(E e) {
linkLast(e);
return true;
}
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++;
}
3.2 add
方法(指定位置插入)
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
void linkBefore(E e, Node<E> succ) {
final Node<E> pred = succ.prev;
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
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;
}
}
3.3 remove
方法
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
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) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
3.4 get
方法
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
四、关键优化技术
4.1 双向遍历优化
- 索引定位:在
node(int index)
方法中,根据索引位置选择从头或尾开始遍历,减少遍历时间。 - 迭代器优化:
ListIterator
支持双向遍历,时间复杂度为 O(n/2)。
4.2 内存局部性
- 节点紧凑:每个节点仅包含前驱、后继指针和元素,内存占用紧凑。
- 缓存友好:通过
node(int index)
的双向遍历策略,提升缓存命中率。
4.3 快速失败机制
- 修改计数器:通过
modCount
字段检测并发修改。 - 迭代器检查:在迭代过程中,若
modCount
变化,抛出ConcurrentModificationException
。
五、典型应用场景
5.1 基础操作
// 创建 LinkedList
List<String> list = new LinkedList<>();
// 尾部插入
list.add("A");
list.add("B");
// 头部插入
list.addFirst("C");
// 尾部删除
list.removeLast();
5.2 队列/栈实现
// 作为队列使用
Queue<String> queue = new LinkedList<>();
queue.offer("A");
String head = queue.poll();
// 作为栈使用
Deque<String> stack = new LinkedList<>();
stack.push("A");
String top = stack.pop();
5.3 双向遍历
// 正向遍历
ListIterator<String> iterator = list.listIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 反向遍历
while (iterator.hasPrevious()) {
System.out.println(iterator.previous());
}
六、总结
JDK 8 的 LinkedList
通过双向链表、双向遍历、动态增长等核心技术,实现了高效的插入和删除操作。其源码实现深刻体现了数据结构与算法的精髓:
- 双向链表:支持 O(1) 时间的插入和删除(已知前驱节点)。
- 内存局部性:通过双向遍历策略,减少遍历时间。
- 快速失败:通过
modCount
检测并发修改,保障迭代安全。
实际开发中,建议根据场景选择数据结构:
- 频繁插入/删除:优先使用
LinkedList
。 - 随机访问:优先使用
ArrayList
。 - 多线程场景:使用
CopyOnWriteArrayList
或通过Collections.synchronizedList
包装。
深入理解其源码,有助于设计高性能的 Java 应用程序。