LinkedHashMap源碼閱讀筆記(基於jdk1.8)

LinkedHashMap是HashMap的子類,不少地方都是直接引用HashMap中的方法,因此須要注意的地方並很少。關鍵的點就是幾個重寫的方法:

一、Entry是繼承與Node類,也就是LinkedHashMap與HashMap的根本區別所在,Node是鏈表形式,只有next與下一個元素進行鏈接,而Entry的鏈表有before和after兩個鏈接點。

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);
        }
    }

二、區別是將節點變成Entry,而且按照鏈表方式將元素有序鏈接

Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMap.Entry<K,V> p =
            new LinkedHashMap.Entry<K,V>(hash, key, value, e);
        linkNodeLast(p);
        return p;
    }

三、紅黑樹節點類型並無改變,也只是按照鏈表方式鏈接

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;
    }
HashMap中爲LinkedHashMap留下了三個預留方法:
一、第一個比較好理解, 在刪除元素e的時候將e先後的元素相連。
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;
    }

二、這個方法表面上看是判斷是否刪除鏈表中第一個元素,可是其實是爲重寫LRU而準備的。node

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);
        }
    }
關鍵就是下面這個方法,在LinkedHashMap中默認返回是false,當須要實現LRU算法的時候繼承LinkedHashMap的子類重寫這個方法便可,LRU內容較多,稍後會單開一貼記錄。
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }
三、第三個方法 主要是將元素移到鏈表的最後一位,關鍵參數是accessOrder,這個參數只有在public LinkedHashMap(int initialCapacity, float loadFactor,boolean accessOrder) 中能夠手動設置爲true,其他時候都默認爲false,因此這個方法實際用到的地方比較少。
void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMap.Entry<K,V> last;
        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;
        }
    }
accessOrder這個參數的實際意義是控制鏈表get的時候是按照插入順序仍是訪問順序,好比下面的例子:
 Map<String,String> m = new LinkedHashMap(16,0.75F,true);
        m.put("1", "a");
        m.put("2", "b");
        m.put("3", "c");
        m.put("4", "d");
        m.get("1");
        m.get("2");
        m.forEach((x,y)->{System.out.println(y);});
這時的輸出是cdab,若是accessOrder設置爲false則輸出abcd。
 
這種鏈表形式其實在調用get方法的時候沒有什麼差異,存儲方式也沒有什麼差異,差異在於forEach這個方法。HashMap中想要遍歷全部元素只能經過三種set集合的迭代器,可是LinkedHashMap自身實現了forEach方法,並且在put的時候隊元素進行了排序,因此能夠直接對自身進行遍歷。
相關文章
相關標籤/搜索