linkedlist的真正优势在于用指针交换代替数据搬移:头尾操作o(1),已知节点删除无需遍历,listiterator支持安全即时删除,边界处理逻辑统一。

LinkedList 在频繁插入删除时的真正优势,不在于“链表比数组快”,而在于它用指针交换代替了数据搬移——尤其是双向结构让关键操作绕开了遍历查找。
头尾操作:纯指针跳转,零搬运
在 LinkedList(底层为双向链表)中,addFirst()、addLast()、removeFirst()、removeLast() 都是 O(1)。原因很简单:头节点和尾节点的引用始终可直接访问,插入只需改两处指针(新节点的 prev/next,原头/尾节点的对应指针),删除同理。没有元素位移,不复制数据,也不触发扩容。
- 对比 ArrayList:在头部 add 一个元素,所有后续元素都要后移一位;remove 第一个元素,其余全部前移——长度越大,代价越高
- 单向链表做头插虽也是 O(1),但尾插仍需遍历到末尾;而双向链表天然持有 tail 引用,尾插同样常数时间
已知节点删除:不用找前驱,直接断链
当已有某个节点的引用(比如通过迭代器定位到某条缓存项),双向链表可立即执行 remove(node),仅需修改该节点前后两个邻居的指针,使其彼此相连。整个过程不查、不遍、不比较值。
- 单向链表做不到这点:要删中间节点,必须从头开始遍历,找到它的前驱才能断链——平均耗时 O(n/2)
- 这正是 LRU 缓存淘汰、消息队列按条件撤回等场景依赖双向链表的核心原因
迭代中安全删除:ListIterator 支持即时解绑
Java 的 ListIterator 在遍历 LinkedList 时,调用 remove() 方法能精准删除刚刚返回的那个节点。因为它内部维护着当前节点和前驱/后继引用,删除时直接更新相邻指针即可,不会破坏迭代状态,也不会引发 ConcurrentModificationException(只要单线程操作)。
- 若用普通 for 循环配合 get(i) 删除,LinkedList 的 get 是 O(n),效率崩盘;而 ListIterator 是为双向链表量身设计的遍历工具
- 注意:不能混用 iterator.remove() 和 list.remove(obj),后者会触发全链查找,退化为 O(n)
空链表与边界处理:自闭环不是必须,但头尾统一很关键
标准 LinkedList 实现中,空链表 head == null,而非指向自身。但关键设计在于:无论是否为空,头插、尾插、头删、尾删的操作逻辑完全一致——不需要 if (isEmpty()) 特判。这是因为插入时总能通过 head/tail 引用直接定位锚点,删除时也只依赖相邻指针是否存在。
- 循环双向链表会让空链表 head.prev == head && head.next == head,进一步简化边界,但 JDK 的 LinkedList 没走这条路
- 真正降低出错率的,是结构带来的操作对称性,而非是否成环











