LinkedList 雙向鏈表源碼分析

目錄

  • LinkedList介紹
  • Node內部類
  • LinkedList源碼分析

LinkedList 介紹

在分析緩存淘汰算法LRU時,雙向鏈表是其中一種實現方式,動手實現時才發現head和tail在沒有其他結點時的處理很彆扭,所以就研究下LinkedList的源碼,找點思路;

首先看看關於LinkedList的簡介

LinkedList 是一個繼承於AbstractSequentialList的雙向鏈表。它也可以被當作堆棧、隊列或雙端隊列進行操作。
LinkedList 實現 List 接口,能對它進行隊列操作。
LinkedList 實現 Deque 接口,即能將LinkedList當作雙端隊列使用。
LinkedList 實現了Cloneable接口,即覆蓋了函數clone(),能克隆。
LinkedList 實現java.io.Serializable接口,這意味着LinkedList支持序列化,能通過序列化去傳輸。
LinkedList 是非同步的。


Node內部類源碼

private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
}

Node類很好理解,是個泛型類,由三要素構成:

  • 當前結點的前繼結點
  • 當前結點的後續結點
  • 當前結點對應的泛型值;

Node結點在創建實例對象時,就需要對其前後結點以及結點對應的值都初始化;


LinkedList 類源碼

注意理解雙向鏈表的核心設計思路,LinkedList的first和last都只是指向鏈表中的node結點實例對象的引用,first和last自身並沒有被創建成單獨的結點對象,理解這一點很關鍵;

實例變量域

// 當前鏈表的元素個數
transient int size = 0;

protected transient int modCount = 0;

// 指向鏈表的第一個結點
transient Node<E> first;

// 指向鏈表的最後一個結點
transient Node<E> last;

 

linkLast方法:向鏈表尾部添加結點

    /**
     * Links e as last element.
     */
    void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

// ------------------ 當向鏈表中首次添加結點時 上述方法可簡化爲:---------------------------
void linkLast(E e) {
        final Node<E> l = last = null;
        final Node<E> newNode = new Node<>(null, e, null);
        last = newNode;
        first = newNode;
        size++;
        modCount++;
}
// ------------------ 當之後再向鏈表中添加結點時 上述方法可簡化爲:-------------------------
void linkLast(E e) {
        final Node<E> newNode = new Node<>(last, e, null);
        final Node<E> l = last;
        last = newNode;
        l.next = newNode;
        size++;
        modCount++;
}

在鏈表爲空時,first和last結點都是指向null;

當第一次向鏈表尾部添加結點時,

  • 創建一個新結點,prev和next都指向null;
  • 由於此時first和last都沒有任何指向(都爲null),所以講first和last都指向該新結點;

當第二次(i > 1)向鏈表尾部添加結點時,

  • 創建一個新結點,prev指向last指向的舊的尾結點,next指向null;
  • last重新指向新尾結點;
  • last之前指向的尾結點,將其next指向新的尾結點;

linkFirst方法:向鏈表頭部添加結點

    /**
     * Links e as first element.
     */
    private void linkFirst(E e) {
        final Node<E> f = first;
        final Node<E> newNode = new Node<>(null, e, f);
        first = newNode;
        if (f == null)
            last = newNode;
        else
            f.prev = newNode;
        size++;
        modCount++;
    }

當第一次向鏈表頭部添加結點時,

  • 創建一個新結點,prev和next都指向null;
  • 由於此時first和last都沒有任何指向(都爲null),所以講first和last都指向該新結點;

當第二次(i > 1)向鏈表頭部添加結點時,

  • 創建一個新結點,prev指向null,next指向first指向的舊的頭結點;
  • first重新指向新的頭結點;
  • 舊的頭結點的prev指向新的頭結點;

unlink方法:從鏈表中移除一個指定的非null結點

    /**
     * Unlinks non-null node x.
     */
    E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;
        final Node<E> next = x.next;
        final Node<E> prev = x.prev;
        
        if (prev == null) {
            // 如果node是第一個結點,就將第二個結點作爲first
            first = next;
        } else {
            // 如果node不是第一個結點,就將前驅結點next指向後繼結點;
            // 並剪斷x對前驅結點的引用,有利於GC回收
            prev.next = next;
            x.prev = null;
        }

        if (next == null) {
            // 如果node是最後一個結點,就將倒數第二個結點作爲最後一個結點
            last = prev;
        } else {
            // 如果node不是最後一個結點,就將後繼結點前驅指向前驅結點;
            // 並簡單x對後繼結點的引用,有利於GC回收
            next.prev = prev;
            x.next = null;
        }
        // 截斷x對item泛型內容的引用,有利於GC回收,至此x結點已經徹底剪斷對所有資源的引用
        x.item = null;
        size--;
        modCount++;
        // 從鏈表中刪除x結點後,返回對x的item內容的引用,以備他用
        return element;
    }

 

remove方法:根據指定的內容,刪除對應的結點

    // remove根據指定的內容o,來移除相應的結點,remove是基於unlink方法實現移除功能;
    // remove支持移除結點item爲null的情景
    public boolean remove(Object o) {
        if (o == null) {
            for (Node<E> x = first; x != null; x = x.next) {
                if (x.item == null) {
                    unlink(x);
                    return true;
                }
            }
        } else {
            for (Node<E> x = first; x != null; x = x.next) {
                if (o.equals(x.item)) {
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

 


參考文獻

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章