JDK1.8源碼閱讀--LinkedHashMap

一、LinkedHashMap的屬性

二、LinkedHashMap的構(gòu)造方法

三、LinkedHashMap的重要函數(shù)

1. afterNodeAccess函數(shù)

2. afterNodeInsertion函數(shù)

3. afterNodeRemoval函數(shù)

4. transferLinks函數(shù)

5. linkNodeLast函數(shù)

6. 其他方法

四、LinkedHashMap的迭代器

1. 基礎(chǔ)迭代器--LinkedHashIterator

2. 鍵迭代器--LinkedKeyIterator

3. 值迭代器--LinkedValueIterator

4. 結(jié)點迭代器--LinkedEntryIterator

LinkedHashMap<K,V>繼承HashMap<K,V>類,實現(xiàn)了 Map<K,V>接口。

LinkedHashMap是HashMap的子類,與HashMap有著同樣的存儲結(jié)構(gòu),但它加入了一個雙向鏈表的頭結(jié)點,將所有put到LinkedHashmap的節(jié)點一一串成了一個雙向循環(huán)鏈表,因此它保留了節(jié)點插入的順序,可以使節(jié)點的輸出順序與輸入順序相同。

LinkedHashMap可以用來實現(xiàn)LRU算法(這會在下面的源碼中進行分析)。

LinkedHashMap同樣是非線程安全的,只在單線程環(huán)境下使用。  

1. LinkedHashMap的屬性

    /**
     * 靜態(tài)內(nèi)部類Entry表示雙向鏈表,繼承自HashMap的Node,在其基礎(chǔ)上加上了before和after兩個指針
     * 雙向循環(huán)鏈表的頭結(jié)點,整個LinkedHashMap中只有一個header
     * 它將哈希表中所有的Entry貫穿起來,header中不保存key-value對,只保存前后節(jié)點的引用  
     */
    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;
    序列化
  • transient LinkedHashMap.Entry<K,V> head;
    鏈表頭結(jié)點
  • transient LinkedHashMap.Entry<K,V> tail;
    鏈表尾結(jié)點
  • final boolean accessOrder;
    雙向鏈表中元素排序規(guī)則的標(biāo)志位;
    false:表示按插入順序排序;true:表示按訪問順序排序

2. LinkedHashMap的構(gòu)造方法

2.1 LinkedHashMap(int, float)型構(gòu)造函數(shù)

    /**
     * 總是會在構(gòu)造函數(shù)的第一行調(diào)用父類構(gòu)造函數(shù),使用super關(guān)鍵字,    
     * accessOrder默認(rèn)為false,access為true表示之后訪問順序按照元素的訪問順序進行
     * 即不按照之前的插入順序了,access為false表示按照插入順序訪問。
     */
    public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }

2.2 LinkedHashMap(int)型構(gòu)造函數(shù)

    /**
     * 加載因子取默認(rèn)的0.75f
     */    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }

2.3 LinkedHashMap()型構(gòu)造函數(shù)

    /**
     * 加載因子取默認(rèn)的0.75f,容量取默認(rèn)的16 
     */
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }

2.4 LinkedHashMap(Map<? extends K, ? extends V>)型構(gòu)造函數(shù)

    /**
     * putMapEntries是調(diào)用到父類HashMap的構(gòu)造函數(shù)
     */
    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }

2.5 LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)型構(gòu)造函數(shù)

    /**
     * 可以指定accessOrder的值,從而控制訪問順序
     */
    public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

3. LinkedHashMap的重要函數(shù)

  • afterNodeAccess、afterNodeInsertion、afterNodeRemoval這三個方法都是重寫父類HashMap的方法。

3.1 afterNodeAccess函數(shù)

    /**
     * 把當(dāng)前節(jié)點e放到雙向鏈表尾部
     */
    void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMap.Entry<K,V> last;
        /* accessOrder就是我們前面說的LRU控制,當(dāng)它為true,同時e對象不是尾節(jié)點
        (如果訪問尾節(jié)點就不需要設(shè)置,該方法就是把節(jié)點放置到尾節(jié)點)
        */
        if (accessOrder && (last = tail) != e) {
            // 用a和b分別記錄該節(jié)點前面和后面的節(jié)點
            LinkedHashMap.Entry<K,V> p =
                (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
            // 釋放當(dāng)前節(jié)點與后節(jié)點的關(guān)系 
            p.after = null;
            // 如果當(dāng)前節(jié)點的前節(jié)點是空
            if (b == null)
                // 那么頭節(jié)點就設(shè)置為a
                head = a;
            else
                // 如果b不為null,那么b的后節(jié)點指向a
                b.after = a;
            // 如果a節(jié)點不為空
            if (a != null)
                // a的后節(jié)點指向b
                a.before = b;
            else
                // 如果a為空,那么b就是尾節(jié)點
                last = b;
            // 如果尾節(jié)點為空
            if (last == null)
                // 那么p為頭節(jié)點
                head = p;
            else {
                // 否則就把p放到雙向鏈表最尾處
                p.before = last;
                last.after = p;
            }
            // 設(shè)置尾節(jié)點為P  
            tail = p;
            // LinkedHashMap對象操作次數(shù)+1
            ++modCount;
        }
    }

此函數(shù)在很多函數(shù)(如put)中都會被回調(diào),LinkedHashMap重寫了HashMap中的此函數(shù)。若訪問順序為true,且訪問的對象不是尾結(jié)點

訪問結(jié)點3的前后狀態(tài)

從圖中可以看到,結(jié)點3鏈接到了尾結(jié)點后面
LinkedHashMap重寫了HashMap的get方法:

    public V get(Object key) {
        Node<K,V> e;
        if ((e = getNode(hash(key), key)) == null)
            return null;
        if (accessOrder)    // 如果啟用了LRU規(guī)則
            afterNodeAccess(e);  // 那么把該節(jié)點移到雙向鏈表最后面
        return e.value;
    }
  • 注: LinkedHashMap壓根沒有重寫put方法,每次用LinkedHashMap的put方法的時候,其實你調(diào)用的都是HashMap的put方法。但它也會執(zhí)行afterNodeAccess()方法,因為這個方法HashMap就是存在的,但是沒有實現(xiàn),LinkedHashMap重寫了afterNodeAccess()這個方法。

3.2 afterNodeInsertion函數(shù)

    /**
     * 在哈希表中插入了一個新節(jié)點時調(diào)用的,它會把鏈表的頭節(jié)點刪除掉,刪除的方式是通過調(diào)用HashMap的removeNode方法
     */
    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);
        }
    }

3.3 afterNodeRemoval函數(shù)

    /**
     * 當(dāng)HashMap刪除一個鍵值對時調(diào)用的,它會把在HashMap中刪除的那個鍵值對一并從鏈表中刪除,保證了哈希表和鏈表的一致性。
     */
    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);
        }
    }

3.4 transferLinks函數(shù)

    /**
     * 用dst替換src
     */
    private void transferLinks(LinkedHashMap.Entry<K,V> src,
                               LinkedHashMap.Entry<K,V> dst) {
        LinkedHashMap.Entry<K,V> b = dst.before = src.before;
        LinkedHashMap.Entry<K,V> a = dst.after = src.after;
        if (b == null)
            head = dst;
        else
            b.after = dst;
        if (a == null)
            tail = dst;
        else
            a.before = dst;
    }
使用結(jié)點7替換結(jié)點3

3.5 linkNodeLast函數(shù)

在看linkNodeLast函數(shù)之前先看看newNode和newTreeNode這兩個函數(shù)

3.5.1 newNode函數(shù)

    /**
     * 當(dāng)桶中結(jié)點類型為HashMap.Node類型時,調(diào)用此函數(shù)
     */
    Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        // 生成Node結(jié)點    
        LinkedHashMap.Entry<K,V> p =
            new LinkedHashMap.Entry<K,V>(hash, key, value, e);
        // 將該結(jié)點插入雙鏈表末尾
        linkNodeLast(p);
        return p;
    }

3.5.2 newTreeNode函數(shù)

    /**
     * 當(dāng)桶中結(jié)點類型為HashMap.TreeNode時,調(diào)用此函數(shù)
     */
    TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
        // 生成TreeNode結(jié)點
        TreeNode<K,V> p = new TreeNode<K,V>(hash, key, value, next);
        // 將該結(jié)點插入雙鏈表末尾
        linkNodeLast(p);
        return p;
    }

3.5.3 linkNodeLast函數(shù)

    /**
     * 把新加的節(jié)點放在鏈表的最后面
     */
    private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        // 將tail給臨時變量last
        LinkedHashMap.Entry<K,V> last = tail;
        // 把new的Entry給tail
        tail = p;
        // 若沒有l(wèi)ast,說明p是第一個節(jié)點,head=p
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
    }

3.6 其他方法

  • internalWriteEntries方法
    /**
     * 該方法也是重寫父類HashMap的方法,也是為了LinkedHashMap鍵和值被序列化的存儲順序
     */
    void internalWriteEntries(java.io.ObjectOutputStream s) throws IOException {
        for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after) {
            s.writeObject(e.key);
            s.writeObject(e.value);
        }
    }

其他方法就不一一介紹的官方API中都有詳細(xì)的說明。

4. LinkedHashMap的迭代器

4.1基礎(chǔ)迭代器--LinkedHashIterator

為抽象類,用于對LinkedHashMap進行迭代

    /**
     * LinkedHashIterator是LinkedHashMap的迭代器,為抽象類,用于對LinkedHashMap進行迭代
     */
    abstract class LinkedHashIterator {
        // 下一個結(jié)點
        LinkedHashMap.Entry<K,V> next;
        // 當(dāng)前結(jié)點
        LinkedHashMap.Entry<K,V> current;
        // 期望的修改次數(shù)
        int expectedModCount;

        LinkedHashIterator() {
            // next賦值為頭結(jié)點
            next = head;
            // 賦值修改次數(shù)
            expectedModCount = modCount;
            // 當(dāng)前結(jié)點賦值為空
            current = null;
        }
        // 是否存在下一個結(jié)點
        public final boolean hasNext() {
            return next != null;
        }

        final LinkedHashMap.Entry<K,V> nextNode() {
            LinkedHashMap.Entry<K,V> e = next;
            // 檢查是否存在結(jié)構(gòu)性修改
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            // 當(dāng)前結(jié)點是否為空
            if (e == null)
                throw new NoSuchElementException();
            // 賦值當(dāng)前節(jié)點
            current = e;
            // 賦值下一個結(jié)點
            next = e.after;
            return e;
        }

        public final void remove() {
            // 保存當(dāng)前結(jié)點
            Node<K,V> p = current;
            if (p == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            current = null;
            K key = p.key;
            // 移除結(jié)點
            removeNode(hash(key), key, null, false, false);
            // 更新最新修改數(shù)
            expectedModCount = modCount;
        }
    }

4.2 鍵迭代器--LinkedKeyIterator

繼承自LinkedHashIterator,實現(xiàn)了Iterator接口,對LinkedHashMap中的鍵進行迭代

    final class LinkedKeyIterator extends LinkedHashIterator
        implements Iterator<K> {
        public final K next() { return nextNode().getKey(); }
    }

4.3 值迭代器--LinkedValueIterator

繼承自LinkedHashIterator,實現(xiàn)了Iterator接口,對LinkedHashMap中的值進行迭代

    final class LinkedValueIterator extends LinkedHashIterator
        implements Iterator<V> {
        public final V next() { return nextNode().value; }
    }

4.4 結(jié)點迭代器--LinkedEntryIterator

繼承自LinkedHashIterator,實現(xiàn)了Iterator接口,對LinkedHashMap中的結(jié)點進行迭代

    final class LinkedEntryIterator extends LinkedHashIterator
        implements Iterator<Map.Entry<K,V>> {
        public final Map.Entry<K,V> next() { return nextNode(); }
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。