LinkedList 的插入删除不总比 ArrayList 快,仅在已知节点引用时 addFirst()/addLast() 等操作为 O(1);否则需 O(n) 查找位置,整体仍为线性时间,且 ArrayList 的 arraycopy 优化和 CPU 缓存局部性使其在多数场景更优。

为什么 LinkedList 的插入删除不总比 ArrayList 快
很多人默认 LinkedList 在任意位置增删都“一定快”,这是错的。它只在**已知节点引用**的前提下,才能做到 O(1) 时间完成插入或删除;而实际编码中,你往往得先 get(i) 或遍历查找目标位置——这个查找过程是 O(n),直接抵消了链表结构的优势。
例如:list.add(500, item) 在 LinkedList 中会先从头/尾出发遍历到索引 500,再操作,整体仍是线性时间;而 ArrayList 虽需数组搬移,但现代 JVM 对 System.arraycopy 有高度优化,小偏移量下甚至更快。
- 用
addFirst()/addLast()/removeFirst()/removeLast()才真正发挥LinkedList优势 -
add(int index, E element)和remove(int index)应视为O(n)操作,不是常数级 - JVM 的 CPU 缓存局部性对
ArrayList更友好,LinkedList的节点分散在堆上,容易触发 cache miss
怎么测才反映真实场景:避免微基准陷阱
用 JMH 测时,如果只测 addLast() 十万次,结果会严重高估链表价值。真实负载往往是混合操作、带查找、受 GC 干扰。关键要模拟你的调用模式:
- 测试必须包含定位开销:比如“找到某个满足条件的节点后插入新节点”,而不是直接传入
Node引用 - 预热要充分(至少 10 轮),并开启
-XX:+UseParallelGC或-XX:+UseZGC减少 GC 波动干扰 - 对比组必须用相同数据规模和操作序列,例如:
for (int i = 0; i —— 这种随机索引删除会让 <code>LinkedList实际变慢
一个常见错误是写这样的测试:
@Benchmark<br>public void addLast(Blackhole bh) {<br> list.addLast(new Object());<br>}它没体现任何查找成本,也没触发内存分配压力,结果毫无参考价值。
什么场景下 LinkedList 真的更合适
它不是“通用更快”,而是为特定访问模式设计的。真正受益的只有这些情况:
- 用作队列或栈:
offerFirst()/pollLast()等双端操作,且不关心随机访问 - 已有迭代器位置(
ListIterator),在遍历时连续插入/删除,例如解析流式 XML 或 token 流处理 - 极端内存受限环境,且插入删除集中在头部/尾部,能接受更高内存占用(每个节点多两个指针 + 对象头)
注意:LinkedList 每个元素额外占用约 24 字节(64 位 JVM,含对象头、prev/next 引用),而 ArrayList 是紧凑数组。当元素本身很小(如 Integer),链表内存开销可达 3 倍以上。
替代方案比死磕 LinkedList 更有效
JDK 自身就在弱化它的存在:Java 21 中 LinkedList 已被标记为 @Deprecated(forRemoval = true)。与其纠结怎么调优它,不如考虑更现代的选择:
- 高频头尾操作 → 改用
ArrayDeque:数组实现、无扩容碎片、cache 友好、支持双端O(1) - 需要稳定迭代中修改 → 用
CopyOnWriteArrayList(读多写少)或ConcurrentLinkedQueue(无锁并发) - 真要随机插入/删除密集 → 考虑
gap buffer或rope结构(如java.util.StringBuilder内部思想),但需自行实现或引入第三方库(如 Eclipse Collections 的MutableList)
别忽略 JIT 编译的影响:简单循环里反复调用 get(i),JVM 可能内联并部分向量化 ArrayList 访问,但对 LinkedList 的遍历几乎无法优化。


















