深入理解LinkedHashMap的實現原理

深入理解LinkedHashMap的實現原理(java8)


概述

在瞭解LinkedHashMap的實現原理之前,先看一下它的源碼介紹:
在這裏插入圖片描述
從註釋中,我們可以先了解到LinkedHashMap是通過哈希表和鏈表實現的,它通過維護一個鏈表來保證對哈希表迭代時的有序性,而這個有序是指鍵值對插入的順序。另外,當向哈希表中重複插入某個鍵的時候,不會影響到原來的有序性。

LinkedHashMap的實現主要分兩部分,一部分是哈希表,另外一部分是鏈表。哈希表部分繼承了HashMap,擁有了HashMap那一套高效的操作,所以我們要看的就是LinkedHashMap中鏈表的部分,瞭解它是如何來維護有序性的。

LinkedHashMap 的大致實現如下圖所示,當然鏈表和哈希表中相同的鍵值對都是指向同一個對象,這裏把它們分開來畫只是爲了呈現出比較清晰的結構。

在這裏插入圖片描述
LinkedHashMap的聲明
在這裏插入圖片描述
LinkedHashMap 是繼承自 HashMap 的,所以它已經從 HashMap 那裏繼承了與哈希表相關的操作了

LinkedHashMap的屬性

關於LinkedHahMap屬性的源碼:

    static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }

    private static final long serialVersionUID = 3801124242820219131L;

    /**
     * The head (eldest) of the doubly linked list.
     */
    transient LinkedHashMap.Entry<K,V> head;

    /**
     * The tail (youngest) of the doubly linked list.
     */
    transient LinkedHashMap.Entry<K,V> tail;

    /**
     * The iteration ordering method for this linked hash map: {@code true}
     * for access-order, {@code false} for insertion-order.
     *
     * @serial
     */
    final boolean accessOrder;

可以發現:LinkedHashMap的鏈表節點繼承了HashMap的節點,而且每個節點都包含了前指針和後指針,所以這裏可以看出它是一個雙向鏈表.

transient LinkedHashMap.Entry<K,V> head;: 頭指針

transient LinkedHashMap.Entry<K,V> tail; : 尾指針

LinkedhashMap的一些方法

其實,在HashMap源碼中有這三個空的方法,其實這三個方法表示的是在訪問、插入、刪除某個節點之後,進行一些處理,它們在LinkedHashMap都有各自的實現。LinkedHashMap正是通過重寫這三個方法來保證鏈表的插入、刪除的有序性。
在這裏插入圖片描述

在LinkedHashMAP中這三者方法的介紹

  • afterNodeAccess():移動節點到尾部
    void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMap.Entry<K,V> last;
        //當accessOrder的值爲true,且e不是尾節點
        if (accessOrder && (last = tail) != e) {
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            p.after = null;
            if (b == null)
                head = a;
            else
                b.after = a;
            if (a != null)
                a.before = b;
            else
                last = b;
            if (last == null)
                head = p;
            else {
                p.before = last;
                last.after = p;
            }
            tail = p;
            ++modCount;
        }
    }

這段代碼的意思簡潔明瞭,就是把當前節點e移至鏈表的尾部。因爲使用的是雙向鏈表,所以在尾部插入可以以O(1)的時間複雜度來完成。並且只有當accessOrder設置爲true時,纔會執行這個操作。在HashMap的putVal方法中,就調用了這個方法

  • afterNodeInsertion():
    void afterNodeInsertion(boolean evict) { // possibly remove eldest
        LinkedHashMap.Entry<K,V> first;
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    }

afterNodeInsertion方法是在哈希表中插入了一個新節點時調用的,它會把鏈表的頭節點刪除掉,刪除的方式是通過調用HashMap的removeNode方法。

  • afterNodeRemoval()
    void afterNodeRemoval(Node<K,V> e) { // unlink
        LinkedHashMap.Entry<K,V> p =
            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        p.before = p.after = null;
        if (b == null)
            head = a;
        else
            b.after = a;
        if (a == null)
            tail = b;
        else
            a.before = b;
    }

這個方法是當HashMap刪除一個鍵值對時調用的,它會把在HashMap中刪除的那個鍵值對一併從鏈表中刪除,保證了哈希表和鏈表的一致性。

  • get()
    public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        if (accessOrder)
            afterNodeAccess(e);
        return e.value;
    }

get()方法,它調用的是HashMap的getNode方法來獲取結果的。

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