LinkedList 源码深度解析(基于双向链表)
前置知识铺垫
LinkedList 的底层核心是 双向链表,每个节点(Node)包含前驱指针(prev)、数据(item)、后继指针(next)。建议先阅读 Java 简单模拟双向链表,理解双向链表的基本操作逻辑。
一、核心结论
LinkedList 维护的是 双向链表结构,通过 first(头节点)、last(尾节点)、size(元素个数)、modCount(修改次数,用于快速失败机制)四个核心属性管理数据。
二、核心组件解析
1. 节点内部类(Node)
双向链表的基础单元,私有静态内部类,封装了节点的核心信息:
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;
}
}
2. 无参构造器
public LinkedList() {}
-
实例化后状态:
-
size=0(无元素) -
first=null(头节点为空) -
last=null(尾节点为空) -
modCount=0(未修改)
-
-
特点:仅初始化链表结构,不分配额外空间,高效轻量。
三、核心方法源码解析
1. 添加元素:add (E e)
默认在链表 尾部添加元素,底层调用 linkLast(E e) 实现。
源码与逐行解析
public boolean add(E e) {
linkLast(e); // 核心逻辑:将元素作为尾节点加入
return true; // 固定返回true(List接口规范,添加成功必返回true)
}
// 核心辅助方法:将e作为最后一个元素链接到链表
void linkLast(E e) {
final Node<E> l = last; // 保存当前尾节点(记为l)
// 新建节点:前驱为l(原尾节点),数据为e,后继为null(新尾节点无后继)
final Node<E> newNode = new Node<>(l, e, null);
last = newNode; // 更新尾节点为新节点
if (l == null) { // 若原尾节点为null → 链表为空,新节点既是头也是尾
first = newNode;
} else { // 链表非空 → 原尾节点的后继指向新节点
l.next = newNode;
}
size++; // 元素个数+1
modCount++; // 修改次数+1(迭代器快速失败的关键)
}
关键逻辑链路
- 空链表添加(l 为 null):
-
①newNode.prev = null 且 ①newNode.next = null
-
③头节点和②尾节点都 = newNode;
- 非空链表添加:
-
①newNode.prev = 原尾节点 且 ③原尾节点.next = newNode(与链表尾建立连接)
-
②尾节点 = newNode。
①②③为代码中顺序
2. 删除元素:remove ()(无参)
默认删除 头节点,底层调用 removeFirst() → unlinkFirst(Node<E> f) 实现。
源码与逐行解析
public E remove() {
return removeFirst(); // 无参删除默认删除头节点
}
// 删除头节点
public E removeFirst() {
final Node<E> f = first; // 保存当前头节点(记为f)
if (f == null) { // 头节点为空 → 链表无元素,抛异常
throw new NoSuchElementException();
}
return unlinkFirst(f); // 核心逻辑:解除头节点的链接
}
// 核心辅助方法:解除非空头节点f的链接(内部调用,确保f≠null)
private E unlinkFirst(Node<E> f) {
// 断言:确保f是头节点且非空(避免外部调用出错)
// assert f == first && f != null;
final E element = f.item; // 保存头节点的数据(用于返回)
final Node<E> next = f.next; // 保存头节点的后继节点(记为next)
// 断开f的引用,帮助GC回收(避免内存泄漏)
f.item = null;
f.next = null;
first = next; // 更新头节点为原头节点的后继(next)
if (next == null) { // next为null → 原链表只有一个节点,删除后链表为空
last = null;
} else { // next非空 → 新头节点的前驱设为null(断开与原头节点的关联)
next.prev = null;
}
size--; // 元素个数-1
modCount++; // 修改次数+1
return element; // 返回被删除的元素数据
}
关键逻辑链路
-
链表仅 1 个元素 → 删除后 first=last=null;
-
链表多个元素 → 新头节点 = 原头节点.next → 新头节点.prev = null → 原头节点被 GC 回收。
注意事项
- 若链表为空(
first=null),调用remove()会抛出NoSuchElementException,需提前判断size>0。 f.item = null和f.next = null是关键:手动断开原头节点的引用,让 GC 识别为垃圾并回收。
四、核心总结
| 核心点 | 细节说明 |
|---|---|
| 数据结构 | 双向链表,节点间通过 prev/next 双向关联 |
| 构造器 | 无参构造器仅初始化空链表,无额外空间开销 |
| add(E e) | 尾插法,时间复杂度 O (1)(直接操作 last 节点) |
| remove() | 头删法,时间复杂度 O (1)(直接操作 first 节点) |
| modCount | 记录结构修改次数,迭代时修改会抛 ConcurrentModificationException |
| GC 优化 | 删除节点时手动断开引用(item/next 设为 null) |