ArrayList随机访问快(O(1)),但中间插入删除慢(O(n));LinkedList增删快(O(1)),但随机访问慢(O(n))、内存开销大、遍历性能差。

ArrayList 随机访问快,但中间插入删除慢
如果你频繁调用 get(i) 或 set(i, e),尤其是索引值不固定、需要跳着读写,ArrayList 是更优选择。它底层是数组,O(1) 时间完成定位。
但要注意:在非尾部位置(比如 add(5, e) 或 remove(10))操作时,它得移动后续所有元素。数据量大时,一次 add(index, e) 可能触发 O(n) 位移 —— 这不是“偶尔慢”,而是每次都会发生。
- 适合场景:日志列表只读展示、缓存预加载后批量遍历、DTO 转换中按序取字段
- 警惕点:别在 for 循环里边遍历边
remove(),容易漏元素或抛ConcurrentModificationException - 小技巧:如果确定容量,用
new ArrayList(initialCapacity)避免多次扩容复制
LinkedList 插入删除快,但随机访问代价高
LinkedList 是双向链表,addFirst()、addLast()、removeFirst()、removeLast() 全是 O(1);在已有节点引用时(比如迭代器刚返回的 next() 节点),addBefore() 或 remove() 也是 O(1)。
但它没有下标支持——每次 get(i) 都得从头或尾开始逐个跳指针,平均 O(n/2)。哪怕只是 get(100),也要遍历 100 次指针跳转,比 ArrayList.get(100) 慢一个数量级。
立即学习“Java免费学习笔记(深入)”;
- 适合场景:实现栈(
push/pop)、队列(offer/poll)、需要频繁在头部增删的事件缓冲区 - 注意陷阱:
list.get(list.size() / 2)看似“取中间”,实则是性能雷区 - 别被名字误导:
LinkedList并不支持高效随机访问,它的get(int)方法内部就是循环遍历
别只看增删查,还要看内存和迭代开销
ArrayList 每个元素只存数据本身(如 Integer 引用),而 LinkedList 每个元素额外包两层对象:一个 Node 实例,含 prev、next、item 三个字段。10 万个元素,LinkedList 多占几 MB 内存很常见。
迭代行为也不同:ArrayList.iterator() 返回的是轻量级内部类,hasNext()/next() 几乎无额外开销;LinkedList.iterator() 每次都要维护当前节点引用,且因 CPU 缓存不友好(节点内存不连续),遍历速度反而更慢。
- 内存敏感场景(如 Android 低配设备、嵌入式 Java):优先
ArrayList - 大量
for (E e : list)场景:两者性能差距比单次get()更明显 -
subList()行为差异:ArrayList.subList()返回的是视图,修改影响原列表;LinkedList.subList()同样是视图,但其add()会触发整段重建,意外开销更大
真要中间插入?先考虑替代方案
如果业务逻辑确实要求“在列表中间高频插入/删除”,LinkedList 很少是最佳解。JVM 对数组操作做了深度优化,而链表破坏了局部性,现代 CPU 架构下反而吃亏。
更实用的做法是:用 ArrayList + 标记删除(逻辑删)、延迟整理;或者改用 TreeSet / LinkedHashSet 维护有序性;甚至直接上 ArrayDeque(如果只需要头尾操作)。
- 别为了“理论复杂度”选
LinkedList,实际压测ArrayList.add(500, e)和LinkedList.add(500, e)在万级数据下的耗时,结果常让人意外 - 如果插入位置有规律(如总在前 10% 或后 10%),可以拆成两个
ArrayList,避免全局位移 - JDK 21 的
SequencedCollection接口统一了顺序集合行为,但底层实现没变——选型逻辑依然取决于你真正怎么用










