arraylist的get()是o(1),linkedlist的get()是o(n);头插linkedlist用addfirst()是o(1),arraylist用add(0,e)是o(n);内存上arraylist更紧凑,linkedlist因额外引用和node对象占用更多内存且gc压力大。

随机访问快不快,看 get(int index) 怎么跑
ArrayList 的 get() 是 O(1),直接算内存偏移就能取;LinkedList 的 get() 是 O(n),得从头或尾一路 next 或 prev 指针跳过去。百万级数据下,linkedList.get(500000) 可能比 arrayList.get(500000) 慢 10 倍以上——不是理论,是实测指针跳跃带来的 CPU 缓存失效和分支预测失败。
- 别在 for 循环里用
linkedList.get(i)遍历,改用增强 for 或iterator() - 如果代码里写着
list.get(i)且i是变量(非固定小索引),默认按 ArrayList 写,换 LinkedList 就埋雷 - 框架里某些泛型工具类(比如 MyBatis 的
ResultHandler)内部会调get(0),看似安全,但若传入 LinkedList,仍会触发一次链表遍历
往开头加元素,addFirst() 和 add(0, e) 完全不是一回事
LinkedList 的 addFirst() 是真 O(1):改 head 指针 + 新建 Node;而 ArrayList 的 add(0, e) 是 O(n),所有已有元素都要往后挪一位——哪怕只加一个字符串,也可能拷贝几万字节的数组。
- 需要高频头插(比如日志缓冲、命令栈),优先用 LinkedList,并显式调
addFirst()或push() - 别用
arrayList.add(0, e)模拟队列头部入队,它比linkedList.addFirst(e)慢得多,且随着 size 增大,性能断崖下跌 - 注意
add(int index, E element)在两个类中签名一样,但行为天差地别:ArrayList 是“移动派”,LinkedList 是“指针派”
内存占用差异,在百万级数据时肉眼可见
ArrayList 存的是纯数据,比如 Integer 数组,每个元素占 4 字节(压缩指针)+ 对象头;LinkedList 每个元素额外带两个引用(prev/next),64 位 JVM 下至少多占 16 字节。100 万个 Integer,ArrayList 约占 4MB,LinkedList 轻松突破 20MB。
- GC 压力更大:LinkedList 创建更多短生命周期对象(Node),更容易触发 Young GC
- 堆外缓存或序列化场景(如 RedisTemplate 存 List),LinkedList 序列化后体积更大、反序列化更慢
- Android 开发尤其敏感:内存紧张时,LinkedList 的额外引用可能直接导致 OOM,而 ArrayList 更“省油”
别被“双向链表支持高效中间删”骗了
LinkedList 中间删除快,前提是——你已经用 ListIterator 定位到了那个节点。如果先 indexOf() 再 remove(),等于白忙:前者遍历一遍找值,后者再遍历一遍找位置,两轮 O(n)。
立即学习“Java免费学习笔记(深入)”;
-
linkedList.remove("xxx")和arrayList.remove("xxx")时间复杂度都是 O(n),没区别 - 真正发挥 LinkedList 中间删优势的写法是:
ListIterator iter = list.listIterator(); while (iter.hasNext()) { if (cond) { iter.remove(); break; } iter.next(); } - 除非你明确控制迭代过程,否则所谓“LinkedList 插入删除快”基本是个幻觉——多数业务代码走的都是基于值或索引的 API,不是基于迭代器的











