关键在底层结构:arraylist是连续内存的动态数组,get为o(1),遍历缓存友好;linkedlist是分散节点的双向链表,get为o(n),增删需先定位,头尾操作o(1)但内存开销大、gc压力高。

选 ArrayList 还是 LinkedList,不能只看“查得快”或“删得多”这种模糊说法。关键在底层结构带来的真实代价:一个是连续内存的数组,一个是分散节点的双向链表。结构不同,性能表现、内存占用、CPU缓存行为都完全不同。
底层结构决定一切
ArrayList 是动态数组:内部用一个 Object[] elementData 存储元素,所有数据在内存中紧挨着。索引访问就是直接算地址(array[index]),所以 get(i) 稳定 O(1)。扩容时按 1.5 倍新建数组并复制,有额外开销,但只要不是高频扩容,影响可控。
LinkedList 是双向链表:每个元素包装成 Node 对象,含 item、prev、next 三个引用。节点在堆上零散分配,get(i) 必须从头或尾开始遍历,JDK 虽做了“靠近哪端走哪端”的优化,但仍是 O(n)。插入删除本身是 O(1),但前提是——你已经拿到那个节点。
别被“增删快”误导:定位成本常被忽略
很多人说“LinkedList 插入快”,其实只说对了一半。真正耗时的往往不是插入动作本身,而是找到插入位置的过程:
- 在末尾加元素:
ArrayList.add(e)和LinkedList.addLast(e)都是 O(1) - 在开头加元素:
ArrayList.add(0, e)要整体移动后续元素 → O(n);LinkedList.addFirst(e)直接改指针 → O(1) - 在中间位置 i 插入:
ArrayList是 O(n) 移动 + O(1) 写入;LinkedList是 O(n) 查找节点 + O(1) 修改指针 → 实际还是 O(n),且缓存不友好,往往更慢
真实场景怎么选?看这三点
优先选 ArrayList 的情况:
- 90% 以上操作是遍历或按索引读取(比如渲染列表、批量计算、DTO 转换)
- 写操作集中在末尾(如日志收集、队列式追加)
- 数据量中等偏大(几千到几十万),且内存局部性重要(CPU 缓存命中率高,实测遍历速度通常是 LinkedList 的 2–5 倍)
考虑 LinkedList 的情况:
- 需要高频在头部/尾部做插入删除,且不依赖索引(比如实现栈、队列、LRU 缓存的链表结构)
- 集合生命周期短、元素少(百级别以内),扩容和对象创建开销差异不明显
- 已存在一个
Node引用,要在此处做增删(例如迭代中调用ListIterator.add())
注意:LinkedList 实现了 Deque 接口,如果你真需要双端队列语义,直接用 ArrayDeque——它底层是循环数组,兼顾了随机访问效率和头尾 O(1) 操作,比 LinkedList 更轻量、更缓存友好。
一个小提醒:内存和 GC 成本
ArrayList 每个元素只存数据,额外开销小;LinkedList 每个元素多两个引用(prev/next),在 64 位 JVM 上至少多占 16 字节,加上对象头、对齐填充,单个节点实际可能比元素本身还大。大量元素时,不仅内存吃紧,GC 压力也更高。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











