Java 链表(LinkedList)

Java 链表(LinkedList)详解:从原理到实战,一篇讲透

LinkedList 是 Java 集合框架中最经典的双向链表实现,同时实现了 ListDeque 接口,常被拿来和 ArrayList 对比。

很多人以为“链表 = 增删快”,但实际使用中很多人踩坑,本文从底层结构、源码核心、时间复杂度、真实场景、与 ArrayList 对比到常见误区,一次性讲清楚。

一、LinkedList 核心特点(一句话总结)

LinkedList 是基于双向链表(doubly-linked list)的线性表结构
支持高效的头尾增删(O(1)),但随机访问(get/set)较慢(O(n))

它同时实现了:

  • List 接口 → 像普通列表使用(有序、可重复、可 null)
  • Deque 接口 → 可以当双端队列队列

二、底层数据结构(最核心)

LinkedList 的内部节点结构(JDK 8+ 源码简化版):

private static class Node<E> {
    E item;              // 存储的元素
    Node<E> next;        // 后继指针
    Node<E> prev;        // 前驱指针
}

三个关键字段:

transient int size = 0;
transient Node<E> first;   // 头节点(头指针)
transient Node<E> last;    // 尾节点(尾指针)

双向链表示意图

null ← prev   [Node1: item=A]   next →   [Node2: item=B]   next →   [Node3: item=C]   next → null
          ↑                                     ↑                                     ↑
        first                                  中间节点                                 last

优点:插入/删除时只改指针,不需要移动元素
缺点:访问第 n 个元素需要从头/尾遍历,内存开销大(每个节点多存两个指针)

三、时间复杂度一览表(面试必背)

操作时间复杂度说明
get(index) / set(index)O(n)需遍历到该位置
add(E e)O(1)尾插(addLast)
addFirst / addLastO(1)头插 / 尾插
add(index, e)O(n)中间插入需遍历
remove(index)O(n)中间删除需遍历
removeFirst / removeLastO(1)头删 / 尾删
contains / indexOfO(n)线性查找
iterator()O(1)顺序遍历很快

结论头尾操作极快,中间随机访问/修改很慢

四、LinkedList vs ArrayList(2024-2025 最新对比)

维度ArrayListLinkedList胜者(常见场景)
底层结构动态数组双向链表
随机访问 get/setO(1)O(n)ArrayList
尾部添加均摊 O(1)(扩容时 O(n))O(1)平手
中间插入/删除O(n)(需移动元素)O(n)(需找到位置)+ O(1) 修改指针LinkedList 略优(大数据量)
内存占用连续内存 + 少量冗余每个节点多两个指针(约 16~24 字节)ArrayList 更省
缓存友好度高(连续内存,CPU 预取)低(节点分散,缓存失效)ArrayList
实际性能(现代测试)几乎全场景完胜(get、add、remove)只在极大量头尾操作时占优ArrayList(大多数情况)

2024-2025 真实测试结论(来自多个 benchmark):

  • 绝大多数场景ArrayList 完胜(包括中间插入删除)
  • LinkedList 只有在频繁头尾增删 + 数据量极大(几十万以上) + 几乎不随机访问时才有优势
  • 现代 JVM + 缓存机制让 ArrayList 的移动元素成本远低于预期

五、LinkedList 的高光使用场景(真正该用的时候)

  1. 当队列 / 双端队列(最常见)
Deque<String> queue = new LinkedList<>();
queue.offer("任务1");          // 队尾入
queue.poll();                  // 队首出
  1. 实现栈(LIFO)
Deque<String> stack = new LinkedList<>();
stack.push("A");
stack.pop();
  1. 频繁在头部插入/删除(如 LRU 缓存的链表部分)
  2. 需要频繁头尾操作且不怎么随机访问(如任务调度队列、消息队列中间层)
  3. 作为队列的默认实现(Java 早期推荐,现在更多用 ArrayDeque)

注意ArrayDeque(数组实现的双端队列)在绝大部分队列/栈场景下性能远超 LinkedList,是现代推荐的替代品。

六、常用方法速查(Deque + List 双重身份)

// List 接口常用
add(E e)          // 尾部添加
get(int index)
set(int index, E element)
remove(int index)
remove(Object o)

// Deque 接口(双端队列)
addFirst / addLast
offerFirst / offerLast
peekFirst / peekLast   // 查看不删除
pollFirst / pollLast   // 删除并返回
push / pop             // 栈操作(等价于 addFirst / removeFirst)

七、常见面试/踩坑问题

  1. LinkedList 是线程安全的吗?
    否!和 ArrayList 一样,多线程下需要自己加锁或用 Collections.synchronizedList() / CopyOnWriteArrayList
  2. 为什么 LinkedList 实现了 Deque?
    因为它是双向链表,天生适合双端操作。
  3. LinkedList 可以存 null 吗?
    可以,而且可以多个 null。
  4. 为什么现在很少用 LinkedList 做普通 List?
    随机访问慢 + 内存开销大 + 缓存不友好,ArrayList 几乎全场景更优。
  5. ArrayDeque vs LinkedList?
    ArrayDeque(数组实现双端队列)在头尾操作更快、内存更省,是 LinkedList 的更好替代品。

八、总结一句话

LinkedList = 双向链表 + List + Deque
优点:头尾增删 O(1),天然支持队列/栈
缺点:随机访问 O(n),内存占用高,缓存不友好
现代建议

  • 普通列表 → 优先 ArrayList
  • 需要队列/栈 → 优先 ArrayDeque
  • 只有极端头尾操作 + 海量数据 + 几乎无随机访问 → 才考虑 LinkedList

想看 LinkedList 源码某个方法详细解析(add / remove / get)、LRU 实现、还是性能测试代码?欢迎继续问~

文章已创建 4455

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部