LinkedHashMap和LruCache源碼分析

LinkedHashMapHashMap的子類,與HashMap有著同樣的存儲(chǔ)結(jié)構(gòu),但它加入了一個(gè)雙向鏈表的頭結(jié)點(diǎn),將所有putLinkedHashMap的節(jié)點(diǎn)一一串成了一個(gè)雙向循環(huán)鏈表,因此它保留了節(jié)點(diǎn)插入的順序。可以按照訪問(wèn)順序和插入順序來(lái)進(jìn)行排序。LruCahe就是基于LinkedHashMap來(lái)實(shí)現(xiàn)的。
(圖片均來(lái)源于網(wǎng)絡(luò))


建議先閱讀HashMap源碼分析在來(lái)閱讀此篇文章。
先看看結(jié)構(gòu)圖,這有助于我們閱讀源碼:


HashMap里面進(jìn)行key value綁定的類是HashMapEntry,在LinkedHashMap則是LinkedHashMapEntry,它繼承HashMapEntry的一個(gè)類并且重寫(xiě)recordAccess recordRemoval來(lái)進(jìn)行重新指向進(jìn)行排序,里面remove函數(shù)主要對(duì)自身在鏈表中進(jìn)行移除,addBefore(LinkedHashMapEntry<K,V> existingEntry)則是將自身插入到existingEntry之前。

 /**
     * LinkedHashMap entry.
     */
    private static class LinkedHashMapEntry<K,V> extends HashMapEntry<K,V> {
        // These fields comprise the doubly linked list used for iteration.
        LinkedHashMapEntry<K,V> before, after;

        LinkedHashMapEntry(int hash, K key, V value, HashMapEntry<K,V> next) {
            super(hash, key, value, next);
        }

        /**
         * Removes this entry from the linked list.
         */
        // 對(duì)自身在鏈表中進(jìn)行移除
        private void remove() {
            before.after = after;
            after.before = before;
        }

        /**
         * Inserts this entry before the specified existing entry in the list.
         */
          //插入到LinkedHashMapEntry existingEntry之前
        private void addBefore(LinkedHashMapEntry<K,V> existingEntry) {
            after  = existingEntry;
            before = existingEntry.before;
            before.after = this;
            after.before = this;
        }

        /**
         * This method is invoked by the superclass whenever the value
         * of a pre-existing entry is read by Map.get or modified by Map.set.
         * If the enclosing Map is access-ordered, it moves the entry
         * to the end of the list; otherwise, it does nothing.
         */
        void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            if (lm.accessOrder) {
                lm.modCount++;
                remove();
                addBefore(lm.header);
            }
        }

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

有了上面的概念來(lái)看LinkedHashMap的操作就相對(duì)比較容易了。
LinkedHashMap put的時(shí)候是調(diào)用HashMapput函數(shù),只是自身實(shí)現(xiàn)了addEntry() createEntry()來(lái)實(shí)現(xiàn)自身的邏輯

HashMap

 /**
     * Associates the specified value with the specified key in this map.
     * If the map previously contained a mapping for the key, the old
     * value is replaced.
     *
     * @param key key with which the specified value is to be associated
     * @param value value to be associated with the specified key
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V put(K key, V value) {
        if (table == EMPTY_TABLE) {
            inflateTable(threshold);
        }
        if (key == null)
            return putForNullKey(value);
        int hash = sun.misc.Hashing.singleWordWangJenkinsHash(key);
        int i = indexFor(hash, table.length);
        for (HashMapEntry<K,V> e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                //調(diào)用子類LinkedHashMapEntry的recordAccess方法
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        //調(diào)用LinkedHashMap.addEntry方法
        addEntry(hash, key, value, i);
        return null;
    }
    
/**
     * Adds a new entry with the specified key, value and hash code to
     * the specified bucket.  It is the responsibility of this
     * method to resize the table if appropriate.
     *
     * Subclass overrides this to alter the behavior of put method.
     */
    void addEntry(int hash, K key, V value, int bucketIndex) {
        //擴(kuò)容
        if ((size >= threshold) && (null != table[bucketIndex])) {
            resize(2 * table.length);
            hash = (null != key) ? sun.misc.Hashing.singleWordWangJenkinsHash(key) : 0;
            bucketIndex = indexFor(hash, table.length);
        }
        //增加新的key  value  調(diào)用LinkedHashMap.createEntry
        createEntry(hash, key, value, bucketIndex);
    }

LinkedHashMap

 /**
     * This override alters behavior of superclass put method. It causes newly
     * allocated entry to get inserted at the end of the linked list and
     * removes the eldest entry if appropriate.
     */
    void addEntry(int hash, K key, V value, int bucketIndex) {
        // Previous Android releases called removeEldestEntry() before actually
        // inserting a value but after increasing the size.
        // The RI is documented to call it afterwards.
        // **** THIS CHANGE WILL BE REVERTED IN A FUTURE ANDROID RELEASE ****
        
        //得到需要移除的對(duì)象
        // Remove eldest entry if instructed
        LinkedHashMapEntry<K,V> eldest = header.after;
        //需要移除的對(duì)象是否是自身
        if (eldest != header) {
            boolean removeEldest;
            size++;
            try {
                  //得到是否移除的對(duì)象的標(biāo)識(shí)
                removeEldest = removeEldestEntry(eldest);
            } finally {
                size--;
            }
            if (removeEldest) {
                //調(diào)用HashMap的removeEntryForKey進(jìn)行移除
                removeEntryForKey(eldest.key);
            }
        }
        //調(diào)用HashMap的addEntry
        super.addEntry(hash, key, value, bucketIndex);
    }
    
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }

    /**
     * Like addEntry except that this version is used when creating entries
     * as part of Map construction or "pseudo-construction" (cloning,
     * deserialization).  This version needn't worry about resizing the table.
     *
     * Subclass overrides this to alter the behavior of HashMap(Map),
     * clone, and readObject.
     */
       //增加新的key  value
    void createEntry(int hash, K key, V value, int bucketIndex) {
        HashMapEntry<K,V> e = table[bucketIndex];
        table[bucketIndex] = new HashMapEntry<>(hash, key, value, e);
        size++;
    }

我們主要看LinkedHashMap里面的東西,涉及到HashMap的就不重復(fù)說(shuō)了。
如果key已經(jīng)在該LinkedHashMap里面put了,那么將會(huì)重新進(jìn)行賦值,并且調(diào)用LinkedHashMapEntry.recordAccess()進(jìn)行排序,排序方式如果是訪問(wèn)順序(accessOrder==true),將會(huì)將其在鏈表中移除,然后添加到鏈尾部。最后在返回oldValue
如果key不存在,則會(huì)調(diào)用LinkedHashMap.addEntry,先得到需要移除的對(duì)象,就是header.after,接著判斷需要移除的對(duì)象是否是自身,如果不是自生并且removeEldestEntry()函數(shù)返回的是true的話,那么將會(huì)調(diào)用HashMapremoveEntryForKey移除。移除這里后面在講。
接著調(diào)用HashMap.addEntry進(jìn)行擴(kuò)容,在調(diào)用LinkedHashMap.createEntry,這一步就是重新new一個(gè)HashMapEntry,添加到鏈尾就可以了。
如圖所示:

添加元素

remove過(guò)程也是調(diào)用HashMap.remove,然后調(diào)用LinkedHashMapEntry.recordRemoval函數(shù)來(lái)進(jìn)行刪除

/**
     * Removes the mapping for the specified key from this map if present.
     *
     * @param  key key whose mapping is to be removed from the map
     * @return the previous value associated with <tt>key</tt>, or
     *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
     *         (A <tt>null</tt> return can also indicate that the map
     *         previously associated <tt>null</tt> with <tt>key</tt>.)
     */
    public V remove(Object key) {
        Entry<K,V> e = removeEntryForKey(key);
        return (e == null ? null : e.getValue());
    }

    /**
     * Removes and returns the entry associated with the specified key
     * in the HashMap.  Returns null if the HashMap contains no mapping
     * for this key.
     */
    final Entry<K,V> removeEntryForKey(Object key) {
        if (size == 0) {
            return null;
        }
        int hash = (key == null) ? 0 : sun.misc.Hashing.singleWordWangJenkinsHash(key);
        int i = indexFor(hash, table.length);
        HashMapEntry<K,V> prev = table[i];
        HashMapEntry<K,V> e = prev;

        while (e != null) {
            HashMapEntry<K,V> next = e.next;
            Object k;
            if (e.hash == hash &&
                ((k = e.key) == key || (key != null && key.equals(k)))) {
                modCount++;
                size--;
                if (prev == e)
                    table[i] = next;
                else
                    prev.next = next;
                //調(diào)用LinkedHashMapEntry.recordRemoval
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }

        return e;
    }

LinkedHashMapEntry中:

/**
         * Removes this entry from the linked list.
         */
        private void remove() {
            before.after = after;
            after.before = before;
        }

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

很簡(jiǎn)單,就是進(jìn)行一個(gè)指向。如圖所示:

刪除元素

get

 /**
     * Returns the value to which the specified key is mapped,
     * or {@code null} if this map contains no mapping for the key.
     *
     * <p>More formally, if this map contains a mapping from a key
     * {@code k} to a value {@code v} such that {@code (key==null ? k==null :
     * key.equals(k))}, then this method returns {@code v}; otherwise
     * it returns {@code null}.  (There can be at most one such mapping.)
     *
     * <p>A return value of {@code null} does not <i>necessarily</i>
     * indicate that the map contains no mapping for the key; it's also
     * possible that the map explicitly maps the key to {@code null}.
     * The {@link #containsKey containsKey} operation may be used to
     * distinguish these two cases.
     */
    public V get(Object key) {
        //調(diào)用HashMap的getEntry
        LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);
        if (e == null)
            return null;
        e.recordAccess(this);
        return e.value;
    }

通過(guò)HashMap.getEntry獲取對(duì)應(yīng)的值,然后調(diào)用LinkedHashMapEntry.recordAccess排序。


有了LinkedHashMap的基礎(chǔ)我們來(lái)看LruCache的源碼就很好理解了

put

 /**
     * Caches {@code value} for {@code key}. The value is moved to the head of
     * the queue.
     *
     * @return the previous value mapped by {@code key}.
     */
    public final V put(K key, V value) {
        if (key == null || value == null) {
            throw new NullPointerException("key == null || value == null");
        }

        V previous;
        synchronized (this) {
            putCount++;
            //根據(jù)key, value獲取大小,我們需要重寫(xiě)sizeOf自行賦值
            size += safeSizeOf(key, value);
            //加入到LinkedHashMap
            previous = map.put(key, value);
            //如果key已經(jīng)存在了previous 就!=null  就移除剛加的大小
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            //重寫(xiě)移除的邏輯 
            entryRemoved(false, key, previous, value);
        }
        //根據(jù)maxSize去除多余的元素
        trimToSize(maxSize);
        return previous;
    }

remove

 /**
     * Removes the entry for {@code key} if it exists.
     *
     * @return the previous value mapped by {@code key}.
     */
    public final V remove(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V previous;
        synchronized (this) {
            //從LinkedHashMap中移除
            previous = map.remove(key);
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            entryRemoved(false, key, previous, null);
        }

        return previous;
    }

get

/**
     * Returns the value for {@code key} if it exists in the cache or can be
     * created by {@code #create}. If a value was returned, it is moved to the
     * head of the queue. This returns null if a value is not cached and cannot
     * be created.
     */
    public final V get(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V mapValue;
        synchronized (this) {
            //從LinkedHashMap中取出元素
            mapValue = map.get(key);
            if (mapValue != null) {
                hitCount++;
                return mapValue;
            }
            missCount++;
        }

        /*
         * Attempt to create a value. This may take a long time, and the map
         * may be different when create() returns. If a conflicting value was
         * added to the map while create() was working, we leave that value in
         * the map and release the created value.
         */
        //如果沒(méi)有這個(gè)key,那么你可以重寫(xiě)create來(lái)創(chuàng)建一個(gè)新的value 
        V createdValue = create(key);
        if (createdValue == null) {
            return null;
        }
      
      //新的創(chuàng)建的value 添加到LinkedHashMap
        synchronized (this) {
            createCount++;
            mapValue = map.put(key, createdValue);

            if (mapValue != null) {
                // There was a conflict so undo that last put
                map.put(key, mapValue);
            } else {
                size += safeSizeOf(key, createdValue);
            }
        }

        if (mapValue != null) {
            entryRemoved(false, key, createdValue, mapValue);
            return mapValue;
        } else {
            trimToSize(maxSize);
            return createdValue;
        }
    }

參考鏈接:
Java容器框架分析(七)——LinkedHashSet與LinkedHashMap
【Java集合源碼剖析】LinkedHashmap源碼剖析

水平有限,文中有什么不對(duì)或者有什么建議希望大家能夠指出,謝謝!

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

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