一句话说透Java里面的LinkedList的工作原理和实现

209 阅读2分钟

LinkedList 就像 一列火车,每节车厢(节点)都连接着前后车厢,可以轻松插入或移除任意位置的车厢。它用双向链表实现,增删快、查询慢,适合频繁修改数据的场景。


一、底层结构:双向链表

每个节点(车厢)包含三部分:

  • 数据:当前节点存储的值。
  • 前驱指针prev):指向前一个节点。
  • 后继指针next):指向后一个节点。

二、核心操作原理

1. 添加元素(add()
  • 尾部追加:直接修改最后一个节点的 next 指针,指向新节点。

  • 中间插入:找到插入位置,调整前后节点的指针。
    示例:在节点A和B之间插入C

    A.next = C;  
    C.prev = A;  
    C.next = B;  
    B.prev = C;  
    
  • 源码关键

    void linkBefore(E e, Node<E> succ) {  
        final Node<E> pred = succ.prev;  
        final Node<E> newNode = new Node<>(pred, e, succ);  
        succ.prev = newNode;  
        if (pred == null)  
            first = newNode;  
        else  
            pred.next = newNode;  
    }  
    
2. 删除元素(remove()
  • 调整指针:将目标节点的前驱和后继直接连接,跳过被删节点。
    示例:删除节点B

    A.next = C;  
    C.prev = A;  
    
  • 源码关键

    E unlink(Node<E> x) {  
        final E element = x.item;  
        final Node<E> next = x.next;  
        final Node<E> prev = x.prev;  
        if (prev == null) {  
            first = next;  
        } else {  
            prev.next = next;  
            x.prev = null;  
        }  
        if (next == null) {  
            last = prev;  
        } else {  
            next.prev = prev;  
            x.next = null;  
        }  
        x.item = null;  
        return element;  
    }  
    
3. 查询元素(get()
  • 遍历链表:从头节点或尾节点开始,逐个移动指针查找(时间复杂度 O(n))。
    示例:获取索引为2的元素

    Node<E> node(int index) {  
        if (index < (size >> 1)) { // 前半部分从头找  
            Node<E> x = first;  
            for (int i = 0; i < index; i++)  
                x = x.next;  
            return x;  
        } else { // 后半部分从尾找  
            Node<E> x = last;  
            for (int i = size - 1; i > index; i--)  
                x = x.prev;  
            return x;  
        }  
    }  
    

三、优缺点对比

优点缺点
头尾增删极快(O(1))随机访问慢(O(n))
中间增删高效(O(1) 找到位置后)内存占用高(每个节点多存两个指针)
无需预分配内存(动态扩容)缓存不友好(内存不连续)

四、适用场景

  1. 频繁增删:如实现队列(Queue)或栈(Stack)。
  2. 中间操作多:如批量插入或删除列表中间的数据。
  3. 内存敏感度低:能接受额外指针的内存开销。

代码示例:用 LinkedList 实现队列

Queue<String> queue = new LinkedList<>();  
queue.offer("任务1"); // 入队  
String task = queue.poll(); // 出队  

五、与 ArrayList 的对比

对比项LinkedListArrayList
底层结构双向链表动态数组
随机访问慢(O(n))快(O(1))
头部增删快(O(1))慢(O(n),需移动元素)
内存占用高(每个节点多两个指针)低(仅需存数据)

六、总结

LinkedList 设计核心

  • 用双向链表灵活管理数据,无需连续内存。
  • 以空间换时间,优化增删操作效率。

口诀
「链表结构像火车,增删只需改指针
查询得从头到尾找,内存占用多一点
队列栈用链式好,数组随机访问强!」