1. 程式人生 > >java集合之LinkedHashMap解析

java集合之LinkedHashMap解析

LinkedHashMap 繼承自HashMap,主要結構還是HashMap,添加了雙向連結串列來保證插入順序或者訪問順序。

著名的LRUCache就是藉助了LinkedHashMap的保持訪問順序的特性實現的。

   //LinkedHashMap中儲存的節點,比hashMap中的Node添加了before,after,node中還有next,
    //新增的兩個節點的指標,組成了雙向連結串列
    static class LinkedHashMapEntry<K,V> extends HashMap.Node<K,V> {
        LinkedHashMapEntry<K,V> before, after;
        LinkedHashMapEntry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }

   //雙向連結串列的頭和尾

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

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

    //true時雙向連結串列訪問順序,false時插入順序
    /**
     * The iteration ordering method for this linked hash map: <tt>true</tt>
     * for access-order, <tt>false</tt> for insertion-order.
     *
     * @serial
     */
    final boolean accessOrder;

newNode函式時hashMap中的函式,LinkedHashMap重寫了這個函式。

//生成一個新節點,然後連線到末尾 
Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMapEntry<K,V> p =
            new LinkedHashMapEntry<K,V>(hash, key, value, e);
        linkNodeLast(p);
        return p;
    }

連線到雙向連結串列尾
 private void linkNodeLast(LinkedHashMapEntry<K,V> p) {
        LinkedHashMapEntry<K,V> last = tail;
        tail = p;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
    }

//treeNode,類似newNode
 TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
        TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
        linkNodeLast(p);
        return p;
    }

這樣就實現了每次插入新節點,放到雙向連結串列尾部。

為了實現linkedHashMap的訪問順序排序,HashMap中預留了三個重要函式,在很多地方被呼叫但是都沒有實現:

// Callbacks to allow LinkedHashMap post-actions
void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }
void afterNodeRemoval(Node<K,V> p) { }

LinkedHashMap中實現了這三個函式:

//操作過陣列和連結串列上的元素之後,修改雙向連結串列,刪除節點
 void afterNodeRemoval(Node<K,V> e) { // unlink
        LinkedHashMapEntry<K,V> p =
            (LinkedHashMapEntry<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;
    }

// 插入新的節點,呼叫過afterNodeAccess之後最終會查詢雙向連結串列刪除相同的key的節點
    void afterNodeInsertion(boolean evict) { // possibly remove eldest
        LinkedHashMapEntry<K,V> first;
        if (evict && (first = head) != null && removeEldestEntry(first)) {
            K key = first.key;
            removeNode(hash(key), key, null, false, true);
        }
    }

//accessOrder== true ,訪問的不是最後一個節點,保證最後一個節點時最近使用的
    void afterNodeAccess(Node<K,V> e) {
        LinkedHashMapEntry<K,V> last;
        if (accessOrder && (last = tail) != e) {
            LinkedHashMapEntry<K,V> p =
                (LinkedHashMapEntry<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;
        }
    }