linkedlist插入删除快、随机访问慢,因其底层为双向链表,增删仅需修改指针(o(1)),而get(int index)需从头或尾遍历定位(o(n));迭代器remove()安全高效,但须先next()后remove()。

LinkedList 为什么插入删除快,但随机访问慢
因为它的底层是双向链表,每个节点都存着 next 和 prev 指针,增删只需改指针,不用搬数据;但找第 n 个元素得从头或尾一路 next/prev 数过去,时间复杂度是 O(n)。
常见错误现象:list.get(10000) 在大列表里卡顿明显,而 list.add(0, x) 却很快——这不是 bug,是结构决定的。
- 使用场景:频繁在头部/尾部增删(比如实现栈、队列),或遍历时边走边删(用
Iterator.remove()) - 随机访问(
get(int index))会自动判断从头还是尾开始遍历,但依然要走 O(n/2) 步 - 别拿它当
ArrayList用,尤其别写for (int i = 0; i
add() 和 addFirst()/addLast() 的实际行为差异
add(E e) 默认等价于 addLast(e),但语义和调用路径不同:前者走的是 AbstractSequentialList 的通用逻辑,后者直触 linkLast(),少一层抽象。
性能上差别微乎其微,但可读性和意图表达更清晰。
-
addFirst(x)→ 调用linkFirst(),直接插到first前,更新first指针 -
addLast(x)→ 调用linkLast(),插到last后,更新last指针 -
add(index, x)会先node(index)查位置,再拆链重连,比首尾插入多一次遍历 - 注意:所有 add 系操作都不检查容量,没有扩容开销,也意味着没预分配空间优化
remove() 调用 node(index) 的隐藏成本
每次按索引删(remove(int index))都会触发 node(index) 查节点,这个方法内部做了“折半查找”:比较 index 和 size/2,决定从头还是从尾开始数。
看起来聪明,但对极端情况不友好:比如删倒数第二个元素,仍要遍历近 n-1 个节点。
- 真正高效的是
removeFirst()、removeLast(),它们直接操作头尾指针,O(1) - 遍历时删除请用
Iterator.remove(),它复用当前节点引用,避免重复查找 - 如果必须按索引删,且操作密集,考虑是否该换数据结构(比如用
ArrayDeque做栈/队列,或用索引映射+标记删除) - 错误示范:
for (int i = list.size()-1; i >= 0; i--) list.remove(i)—— 每次都从头开始找,总耗时 O(n²)
迭代器遍历时 remove() 的安全边界
LinkedList 的 Iterator 是 fail-fast 的,但它的 remove() 是唯一被允许的结构性修改方式,且能保证 O(1) 完成。
关键在于:它删的是上一次 next() 返回的节点,直接用当前 Node 引用改指针,不重新查位置。
- 必须先调用
next(),才能调用remove(),否则抛IllegalStateException - 不能连续调两次
remove(),中间必须穿插next() - 用增强 for 循环无法调用
Iterator.remove(),所以这种写法本质是线程不安全的:for (String s : list) if (s.isEmpty()) list.remove(s) - 替代方案:用
listIterator()可双向遍历 + 删除,适合需要前后扫描的场景
双向链表的“高效”是有条件的:只在指针能直接触达的位置才成立。一旦涉及索引定位,就退化为链式扫描——这点很容易被 API 表面的简洁掩盖。









