LinkedHashMap源碼分析

以下內(nèi)容整理自互聯(lián)網(wǎng),僅用于個人學(xué)習(xí)


LinkedHashMap簡介

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

LinkedHashMap可以用來實現(xiàn)LRU算法。

LinkedHashMap是非線程安全的。


LinkedHashMap源碼分析

package java.util;  
import java.io.*;  


public class LinkedHashMap<K,V>  
    extends HashMap<K,V>  
    implements Map<K,V>  
{  

    private static final long serialVersionUID = 3801124242820219131L;  

    //雙向循環(huán)鏈表的頭結(jié)點,整個LinkedHashMap中只有一個header,  
    //它將哈希表中所有的Entry貫穿起來,header中不保存key-value對,只保存前后節(jié)點的引用  
    private transient Entry<K,V> header;  

    //雙向鏈表中元素排序規(guī)則的標(biāo)志位。  
    //accessOrder為false,表示按插入順序排序  
    //accessOrder為true,表示按訪問順序排序  
    private final boolean accessOrder;  

    //調(diào)用HashMap的構(gòu)造方法來構(gòu)造底層的數(shù)組  
    public LinkedHashMap(int initialCapacity, float loadFactor) {  
        super(initialCapacity, loadFactor);  
        accessOrder = false;    //鏈表中的元素默認(rèn)按照插入順序排序  
    }  

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

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

    //含有子Map的構(gòu)造方法,同樣調(diào)用HashMap的對應(yīng)的構(gòu)造方法  
    public LinkedHashMap(Map<? extends K, ? extends V> m) {  
        super(m);  
        accessOrder = false;  
    }  

    //該構(gòu)造方法可以指定鏈表中的元素排序的規(guī)則  
    public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) {  
        super(initialCapacity, loadFactor);  
        this.accessOrder = accessOrder;  
    }  

    //覆寫父類的init()方法(HashMap中的init方法為空),  
    //該方法在父類的構(gòu)造方法和Clone、readObject中在插入元素前被調(diào)用,  
    //初始化一個空的雙向循環(huán)鏈表,頭結(jié)點中不保存數(shù)據(jù),頭結(jié)點的下一個節(jié)點才開始保存數(shù)據(jù)。  
    void init() {  
        header = new Entry<K,V>(-1, null, null, null);  
        header.before = header.after = header;  
    }  


    //覆寫HashMap中的transfer方法,它在父類的resize方法中被調(diào)用,  
    //擴容后,將key-value對重新映射到新的newTable中  
    //覆寫該方法的目的是為了提高復(fù)制的效率,  
    //這里充分利用雙向循環(huán)鏈表的特點進(jìn)行迭代,不用對底層的數(shù)組進(jìn)行for循環(huán)。  
    void transfer(HashMap.Entry[] newTable) {  
        int newCapacity = newTable.length;  
        for (Entry<K,V> e = header.after; e != header; e = e.after) {  
            int index = indexFor(e.hash, newCapacity);  
            e.next = newTable[index];  
            newTable[index] = e;  
        }  
    }  


    //覆寫HashMap中的containsValue方法,  
    //覆寫該方法的目的同樣是為了提高查詢的效率,  
    //利用雙向循環(huán)鏈表的特點進(jìn)行查詢,少了對數(shù)組的外層for循環(huán)  
    public boolean containsValue(Object value) {  
        // Overridden to take advantage of faster iterator  
        if (value==null) {  
            for (Entry e = header.after; e != header; e = e.after)  
                if (e.value==null)  
                    return true;  
        } else {  
            for (Entry e = header.after; e != header; e = e.after)  
                if (value.equals(e.value))  
                    return true;  
        }  
        return false;  
    }  


    //覆寫HashMap中的get方法,通過getEntry方法獲取Entry對象。  
    //注意這里的recordAccess方法,  
    //如果鏈表中元素的排序規(guī)則是按照插入的先后順序排序的話,該方法什么也不做,  
    //如果鏈表中元素的排序規(guī)則是按照訪問的先后順序排序的話,則將e移到鏈表的末尾處。  
    public V get(Object key) {  
        Entry<K,V> e = (Entry<K,V>)getEntry(key);  
        if (e == null)  
            return null;  
        e.recordAccess(this);  
        return e.value;  
    }  

    //清空HashMap,并將雙向鏈表還原為只有頭結(jié)點的空鏈表  
    public void clear() {  
        super.clear();  
        header.before = header.after = header;  
    }  

    //Enty的數(shù)據(jù)結(jié)構(gòu),多了兩個指向前后節(jié)點的引用  
    private static class Entry<K,V> extends HashMap.Entry<K,V> {  
        // These fields comprise the doubly linked list used for iteration.  
        Entry<K,V> before, after;  

        //調(diào)用父類的構(gòu)造方法  
        Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {  
            super(hash, key, value, next);  
        }  

        //雙向循環(huán)鏈表中,刪除當(dāng)前的Entry  
        private void remove() {  
            before.after = after;  
            after.before = before;  
        }  

        //雙向循環(huán)立鏈表中,將當(dāng)前的Entry插入到existingEntry的前面  
        private void addBefore(Entry<K,V> existingEntry) {  
            after  = existingEntry;  
            before = existingEntry.before;  
            before.after = this;  
            after.before = this;  
        }  


        //覆寫HashMap中的recordAccess方法(HashMap中該方法為空),  
        //當(dāng)調(diào)用父類的put方法,在發(fā)現(xiàn)插入的key已經(jīng)存在時,會調(diào)用該方法,  
        //調(diào)用LinkedHashmap覆寫的get方法時,也會調(diào)用到該方法,  
        //該方法提供了LRU算法的實現(xiàn),它將最近使用的Entry放到雙向循環(huán)鏈表的尾部,  
        //accessOrder為true時,get方法會調(diào)用recordAccess方法  
        //put方法在覆蓋key-value對時也會調(diào)用recordAccess方法  
        //它們導(dǎo)致Entry最近使用,因此將其移到雙向鏈表的末尾  
        void recordAccess(HashMap<K,V> m) {  
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;  
            //如果鏈表中元素按照訪問順序排序,則將當(dāng)前訪問的Entry移到雙向循環(huán)鏈表的尾部,  
            //如果是按照插入的先后順序排序,則不做任何事情。  
            if (lm.accessOrder) {  
                lm.modCount++;  
                //移除當(dāng)前訪問的Entry  
                remove();  
                //將當(dāng)前訪問的Entry插入到鏈表的尾部  
                addBefore(lm.header);  
            }  
        }  

        void recordRemoval(HashMap<K,V> m) {  
            remove();  
        }  
    }  

    //迭代器  
    private abstract class LinkedHashIterator<T> implements Iterator<T> {  
    Entry<K,V> nextEntry    = header.after;  
    Entry<K,V> lastReturned = null;  

    /** 
     * The modCount value that the iterator believes that the backing 
     * List should have.  If this expectation is violated, the iterator 
     * has detected concurrent modification. 
     */  
    int expectedModCount = modCount;  

    public boolean hasNext() {  
            return nextEntry != header;  
    }  

    public void remove() {  
        if (lastReturned == null)  
        throw new IllegalStateException();  
        if (modCount != expectedModCount)  
        throw new ConcurrentModificationException();  

            LinkedHashMap.this.remove(lastReturned.key);  
            lastReturned = null;  
            expectedModCount = modCount;  
    }  

    //從head的下一個節(jié)點開始迭代  
    Entry<K,V> nextEntry() {  
        if (modCount != expectedModCount)  
        throw new ConcurrentModificationException();  
            if (nextEntry == header)  
                throw new NoSuchElementException();  

            Entry<K,V> e = lastReturned = nextEntry;  
            nextEntry = e.after;  
            return e;  
    }  
    }  

    //key迭代器  
    private class KeyIterator extends LinkedHashIterator<K> {  
    public K next() { return nextEntry().getKey(); }  
    }  

    //value迭代器  
    private class ValueIterator extends LinkedHashIterator<V> {  
    public V next() { return nextEntry().value; }  
    }  

    //Entry迭代器  
    private class EntryIterator extends LinkedHashIterator<Map.Entry<K,V>> {  
    public Map.Entry<K,V> next() { return nextEntry(); }  
    }  

    // These Overrides alter the behavior of superclass view iterator() methods  
    Iterator<K> newKeyIterator()   { return new KeyIterator();   }  
    Iterator<V> newValueIterator() { return new ValueIterator(); }  
    Iterator<Map.Entry<K,V>> newEntryIterator() { return new EntryIterator(); }  


    //覆寫HashMap中的addEntry方法,LinkedHashmap并沒有覆寫HashMap中的put方法,  
    //而是覆寫了put方法所調(diào)用的addEntry方法和recordAccess方法,  
    //put方法在插入的key已存在的情況下,會調(diào)用recordAccess方法,  
    //在插入的key不存在的情況下,要調(diào)用addEntry插入新的Entry  
    void addEntry(int hash, K key, V value, int bucketIndex) {  
        //創(chuàng)建新的Entry,并插入到LinkedHashMap中  
        createEntry(hash, key, value, bucketIndex);  

        //雙向鏈表的第一個有效節(jié)點(header后的那個節(jié)點)為近期最少使用的節(jié)點  
        Entry<K,V> eldest = header.after;  
        //如果有必要,則刪除掉該近期最少使用的節(jié)點,  
        //這要看對removeEldestEntry的覆寫,由于默認(rèn)為false,因此默認(rèn)是不做任何處理的。  
        if (removeEldestEntry(eldest)) {  
            removeEntryForKey(eldest.key);  
        } else {  
            //擴容到原來的2倍  
            if (size >= threshold)  
                resize(2 * table.length);  
        }  
    }  

    void createEntry(int hash, K key, V value, int bucketIndex) {  
        //創(chuàng)建新的Entry,并將其插入到數(shù)組對應(yīng)槽的單鏈表的頭結(jié)點處,這點與HashMap中相同  
        HashMap.Entry<K,V> old = table[bucketIndex];  
        Entry<K,V> e = new Entry<K,V>(hash, key, value, old);  
        table[bucketIndex] = e;  
        //每次插入Entry時,都將其移到雙向鏈表的尾部,  
        //這便會按照Entry插入LinkedHashMap的先后順序來迭代元素,  
        //同時,新put進(jìn)來的Entry是最近訪問的Entry,把其放在鏈表末尾 ,符合LRU算法的實現(xiàn)  
        e.addBefore(header);  
        size++;  
    }  

    //該方法是用來被覆寫的,一般如果用LinkedHashmap實現(xiàn)LRU算法,就要覆寫該方法,  
    //比如可以將該方法覆寫為如果設(shè)定的內(nèi)存已滿,則返回true,這樣當(dāng)再次向LinkedHashMap中put  
    //Entry時,在調(diào)用的addEntry方法中便會將近期最少使用的節(jié)點刪除掉(header后的那個節(jié)點)。  
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {  
        return false;  
    }  
}

總結(jié)

  • 實際上就是HashMap和LinkedList兩個集合類的存儲結(jié)構(gòu)的結(jié)合。在LinkedHashMapMap中,所有put進(jìn)來的Entry都保存在哈希表中,但它又額外定義了一個以head為頭結(jié)點的空的雙向循環(huán)鏈表,每次put進(jìn)來Entry,除了將其保存到對哈希表中對應(yīng)的位置上外,還要將其插入到雙向循環(huán)鏈表的尾部。

  • LinkedHashMap由于繼承自HashMap,因此它具有HashMap的所有特性,同樣允許key和value為null。

  • 注意源碼中的accessOrder標(biāo)志位,當(dāng)它false時,表示雙向鏈表中的元素按照Entry插入LinkedHashMap到中的先后順序排序,即每次put到LinkedHashMap中的Entry都放在雙向鏈表的尾部,這樣遍歷雙向鏈表時,Entry的輸出順序便和插入的順序一致,這也是默認(rèn)的雙向鏈表的存儲順序;當(dāng)它為true時,表示雙向鏈表中的元素按照訪問的先后順序排列,可以看到,雖然Entry插入鏈表的順序依然是按照其put到LinkedHashMap中的順序,但put和get方法均有調(diào)用recordAccess方法(put方法在key相同,覆蓋原有的Entry的情況下調(diào)用recordAccess方法),該方法判斷accessOrder是否為true,如果是,則將當(dāng)前訪問的Entry(put進(jìn)來的Entry或get出來的Entry)移到雙向鏈表的尾部(key不相同時,put新Entry時,會調(diào)用addEntry,它會調(diào)用creatEntry,該方法同樣將新插入的元素放入到雙向鏈表的尾部,既符合插入的先后順序,又符合訪問的先后順序,因為這時該Entry也被訪問了),否則,什么也不做。

  • 注意構(gòu)造方法,前四個構(gòu)造方法都將accessOrder設(shè)為false,說明默認(rèn)是按照插入順序排序的,而第五個構(gòu)造方法可以自定義傳入的accessOrder的值,因此可以指定雙向循環(huán)鏈表中元素的排序規(guī)則,一般要用LinkedHashMap實現(xiàn)LRU算法,就要用該構(gòu)造方法,將accessOrder置為true。

  • LinkedHashMap并沒有覆寫HashMap中的put方法,而是覆寫了put方法中調(diào)用的addEntry方法和recordAccess方法,且LinkedHashMap覆寫了HashMap的get方法。

  • LinkedHashMap是如何實現(xiàn)LRU的。首先,當(dāng)accessOrder為true時,才會開啟按訪問順序排序的模式,才能用來實現(xiàn)LRU算法。我們可以看到,無論是put方法還是get方法,都會導(dǎo)致目標(biāo)Entry成為最近訪問的Entry,因此便把該Entry加入到了雙向鏈表的末尾(get方法通過調(diào)用recordAccess方法來實現(xiàn),put方法在覆蓋已有key的情況下,也是通過調(diào)用recordAccess方法來實現(xiàn),在插入新的Entry時,則是通過createEntry中的addBefore方法來實現(xiàn)),這樣便把最近使用了的Entry放入到了雙向鏈表的后面,多次操作后,雙向鏈表前面的Entry便是最近沒有使用的,這樣當(dāng)節(jié)點個數(shù)滿的時候,刪除的最前面的Entry(head后面的那個Entry)便是最近最少使用的Entry。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容