LinkedHashMap源码分析

来源:互联网 发布:php代码美化 编辑:程序博客网 时间:2024/06/07 00:44

参考:【Java集合源码剖析】LinkedHashmap源码剖析

entry: [ˈentrɪ] 进入;入口

LinkedHashMap是HashMap的子类,与HashMap有着同样的存储结构,但它加入了一个双向链表的头结点,将所有put到LinkedHashmap的节点一一串成了一个双向循环链表,因此它保留了节点插入的顺序,可以使节点的输出顺序与输入顺序相同。

LinkedHashMap可以用来实现LRU算法。

LinkedHashMap同样是非线程安全的,只在单线程环境下使用。

package java.util;import sun.misc.Hashing;import java.io.*;import java.util.function.BiFunction;import java.util.function.Consumer;import java.util.function.BiConsumer;public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>{    private static final long serialVersionUID = 3801124242820219131L;    /**     * The head of the doubly linked list.     */    //双向循环链表的头结点,整个LinkedHashMap中只有一个header,      //它将哈希表中所有的Entry贯穿起来,header中不保存key-value对,只保存前后节点的引用      private transient LinkedHashMapEntry<K,V> header;    /**     * The iteration ordering method for this linked hash map: <tt>true</tt>     * for access-order, <tt>false</tt> for insertion-order.     *     * @serial     */    //双向链表中元素排序规则的标志位。      //accessOrder为false,表示按插入顺序排序      //accessOrder为true,表示按访问顺序排序     private final boolean accessOrder;    //---------------------------------构造方法开始--------------------------------------    /**     * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance     * with the specified initial capacity and load factor.     *     * @param  initialCapacity the initial capacity     * @param  loadFactor      the load factor     * @throws IllegalArgumentException if the initial capacity is negative     *         or the load factor is nonpositive     */    //调用HashMap的构造方法来构造底层的数组     public LinkedHashMap(int initialCapacity, float loadFactor) {        super(initialCapacity, loadFactor);        accessOrder = false;//链表中的元素默认按照插入顺序排序     }    /**     * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance     * with the specified initial capacity and a default load factor (0.75).     *     * @param  initialCapacity the initial capacity     * @throws IllegalArgumentException if the initial capacity is negative     */     //加载因子取默认的0.75f     public LinkedHashMap(int initialCapacity) {        super(initialCapacity);        accessOrder = false;    }    /**     * Constructs an empty insertion-ordered <tt>LinkedHashMap</tt> instance     * with the default initial capacity (16) and load factor (0.75).     */     //加载因子取默认的0.75f,容量取默认的16    public LinkedHashMap() {        super();        accessOrder = false;    }    /**     * Constructs an insertion-ordered <tt>LinkedHashMap</tt> instance with     * the same mappings as the specified map.  The <tt>LinkedHashMap</tt>     * instance is created with a default load factor (0.75) and an initial     * capacity sufficient to hold the mappings in the specified map.     *     * @param  m the map whose mappings are to be placed in this map     * @throws NullPointerException if the specified map is null     */     //含有子Map的构造方法,同样调用HashMap的对应的构造方法    public LinkedHashMap(Map<? extends K, ? extends V> m) {        super(m);        accessOrder = false;    }    /**     * Constructs an empty <tt>LinkedHashMap</tt> instance with the     * specified initial capacity, load factor and ordering mode.     *     * @param  initialCapacity the initial capacity     * @param  loadFactor      the load factor     * @param  accessOrder     the ordering mode - <tt>true</tt> for     *         access-order, <tt>false</tt> for insertion-order     * @throws IllegalArgumentException if the initial capacity is negative     *         or the load factor is nonpositive     */     //该构造方法可以指定链表中的元素排序的规则    public LinkedHashMap(int initialCapacity,                         float loadFactor,                         boolean accessOrder) {        super(initialCapacity, loadFactor);        this.accessOrder = accessOrder;    }    //---------------------------------构造方法结束--------------------------------------    /**     * Called by superclass constructors and pseudoconstructors (clone,     * readObject) before any entries are inserted into the map.  Initializes     * the chain.     */     //覆写父类的init()方法(HashMap中的init方法为空),      //该方法在父类的构造方法和Clone、readObject中在插入元素前被调用,      //初始化一个空的双向循环链表,头结点中不保存数据,头结点的下一个节点才开始保存数据。      @Override    void init() {        header = new LinkedHashMapEntry<>(-1, null, null, null);        header.before = header.after = header;    }    /**     * Transfers all entries to new table array.  This method is called     * by superclass resize.  It is overridden for performance, as it is     * faster to iterate using our linked list.     */      //覆写HashMap中的transfer方法,它在父类的resize方法中被调用,      //扩容后,将key-value对重新映射到新的newTable中      //覆写该方法的目的是为了提高复制的效率,      //这里充分利用双向循环链表的特点进行迭代,不用对底层的数组进行for循环。      @Override    void transfer(HashMapEntry[] newTable) {        int newCapacity = newTable.length;        for (LinkedHashMapEntry<K,V> e = header.after; e != header; e = e.after) {            int index = indexFor(e.hash, newCapacity);            e.next = newTable[index];            newTable[index] = e;        }    }    /**     * Returns <tt>true</tt> if this map maps one or more keys to the     * specified value.     *     * @param value value whose presence in this map is to be tested     * @return <tt>true</tt> if this map maps one or more keys to the     *         specified value     */     //覆写HashMap中的containsValue方法,      //覆写该方法的目的同样是为了提高查询的效率,      //利用双向循环链表的特点进行查询,少了对数组的外层for循环      public boolean containsValue(Object value) {        // Overridden to take advantage of faster iterator        if (value==null) {            for (LinkedHashMapEntry e = header.after; e != header; e = e.after)                if (e.value==null)                    return true;        } else {            for (LinkedHashMapEntry e = header.after; e != header; e = e.after)                if (value.equals(e.value))                    return true;        }        return false;    }    /**     * 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.     */     //覆写HashMap中的get方法,通过getEntry方法获取Entry对象。      //注意这里的recordAccess方法,      //如果链表中元素的排序规则是按照插入的先后顺序排序的话,该方法什么也不做,      //如果链表中元素的排序规则是按照访问的先后顺序排序的话,则将e移到链表的末尾处。      public V get(Object key) {        LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);        if (e == null)            return null;        e.recordAccess(this);//取得Entry,如果不为null,调用recordAccess方法        return e.value;    }    /**     * Removes all of the mappings from this map.     * The map will be empty after this call returns.     */     //清空HashMap,并将双向链表还原为只有头结点的空链表     public void clear() {        super.clear();        header.before = header.after = header;    }    /**     * LinkedHashMap entry.     */     //Enty的数据结构,多了两个指向前后节点的引用     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.         */        //双向循环链表中,删除当前的Entry        private void remove() {            before.after = after;            after.before = before;        }        /**         * Inserts this entry before the specified existing entry in the list.         */         //双向循环立链表中,将当前的Entry插入到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.         */         //覆写HashMap中的recordAccess方法(HashMap中该方法为空),          //当调用父类的put方法,在发现插入的key已经存在时,会调用该方法,          //调用LinkedHashmap覆写的get方法时,也会调用到该方法,          //该方法提供了LRU算法的实现,它将最近使用的Entry放到双向循环链表的尾部,          //accessOrder为true时,get方法会调用recordAccess方法          //put方法在覆盖key-value对时也会调用recordAccess方法          //它们导致Entry最近使用,因此将其移到双向链表的末尾         void recordAccess(HashMap<K,V> m) {            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;            //如果链表中元素按照访问顺序排序,则将当前访问的Entry移到双向循环链表的尾部,              //如果是按照插入的先后顺序排序,则不做任何事情。            if (lm.accessOrder) {                lm.modCount++;                remove(); //移除当前访问的Entry                 addBefore(lm.header);//将当前访问的Entry插入到链表的尾部            }        }        void recordRemoval(HashMap<K,V> m) {            remove();        }    }    //迭代器     private abstract class LinkedHashIterator<T> implements Iterator<T> {        LinkedHashMapEntry<K,V> nextEntry    = header.after;        LinkedHashMapEntry<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的下一个节点开始迭代         Entry<K,V> nextEntry() {            if (modCount != expectedModCount)                throw new ConcurrentModificationException();            if (nextEntry == header)                throw new NoSuchElementException();            LinkedHashMapEntry<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().getValue(); }    }    //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(); }    /**     * 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 ****        // Remove eldest entry if instructed        LinkedHashMapEntry<K,V> eldest = header.after;        if (eldest != header) {            boolean removeEldest;            size++;            try {                removeEldest = removeEldestEntry(eldest);            } finally {                size--;            }            if (removeEldest) {                removeEntryForKey(eldest.key);            }        }        super.addEntry(hash, key, value, bucketIndex);    }    /**     * Returns the eldest entry in the map, or {@code null} if the map is empty.     *     * Android-added.     *     * @hide     */     //提供最近最少使用的元素:    public Map.Entry<K, V> eldest() {        Entry<K, V> eldest = header.after;        return eldest != header ? eldest : null;    }    /**     * This override differs from addEntry in that it doesn't resize the     * table or remove the eldest entry.     */    void createEntry(int hash, K key, V value, int bucketIndex) {        HashMapEntry<K,V> old = table[bucketIndex];        LinkedHashMapEntry<K,V> e = new LinkedHashMapEntry<>(hash, key, value, old);        table[bucketIndex] = e;        e.addBefore(header);        size++;    }    // Intentionally make this not JavaDoc, as the we don't conform to    // the behaviour documented here (we call removeEldestEntry before    // inserting the new value to be consistent with previous Android    // releases).    // **** THIS CHANGE WILL BE REVERTED IN A FUTURE ANDROID RELEASE ****    /*     * Returns <tt>true</tt> if this map should remove its eldest entry.     * This method is invoked by <tt>put</tt> and <tt>putAll</tt> after     * inserting a new entry into the map.  It provides the implementor     * with the opportunity to remove the eldest entry each time a new one     * is added.  This is useful if the map represents a cache: it allows     * the map to reduce memory consumption by deleting stale entries.     *     * <p>Sample use: this override will allow the map to grow up to 100     * entries and then delete the eldest entry each time a new entry is     * added, maintaining a steady state of 100 entries.     * <pre>     *     private static final int MAX_ENTRIES = 100;     *     *     protected boolean removeEldestEntry(Map.Entry eldest) {     *        return size() > MAX_ENTRIES;     *     }     * </pre>     *     * <p>This method typically does not modify the map in any way,     * instead allowing the map to modify itself as directed by its     * return value.  It <i>is</i> permitted for this method to modify     * the map directly, but if it does so, it <i>must</i> return     * <tt>false</tt> (indicating that the map should not attempt any     * further modification).  The effects of returning <tt>true</tt>     * after modifying the map from within this method are unspecified.     *     * <p>This implementation merely returns <tt>false</tt> (so that this     * map acts like a normal map - the eldest element is never removed).     *     * @param    eldest The least recently inserted entry in the map, or if     *           this is an access-ordered map, the least recently accessed     *           entry.  This is the entry that will be removed it this     *           method returns <tt>true</tt>.  If the map was empty prior     *           to the <tt>put</tt> or <tt>putAll</tt> invocation resulting     *           in this invocation, this will be the entry that was just     *           inserted; in other words, if the map contains a single     *           entry, the eldest entry is also the newest.     * @return   <tt>true</tt> if the eldest entry should be removed     *           from the map; <tt>false</tt> if it should be retained.     */      //该方法是用来被覆写的,一般如果用LinkedHashmap实现LRU算法,就要覆写该方法,      //比如可以将该方法覆写为如果设定的内存已满,则返回true,这样当再次向LinkedHashMap中put      //Entry时,在调用的addEntry方法中便会将近期最少使用的节点删除掉(header后的那个节点)。  //该方法默认返回false,我们一般在用LinkedHashMap实现LRU算法时,要覆写该方法,      //一般的实现是,当设定的内存(这里指节点个数)达到最大值时,返回true,    //这样put新的Entry(该Entry的key在哈希表中没有已经存在)时,就会调用removeEntryForKey方法,    //将最近最少使用的节点删除(head后面的那个节点,实际上是最近没有使用)。    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {        return false;    }    // Map overrides    public void forEach(BiConsumer<? super K, ? super V> action) {        if (action == null)            throw new NullPointerException();        int mc = modCount;        // Android modified - breaks from the loop when modCount != mc        for (LinkedHashMapEntry<K,V> e = header.after; modCount == mc && e != header; e = e.after)            action.accept(e.key, e.value);        if (modCount != mc)            throw new ConcurrentModificationException();    }    public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {        if (function == null)            throw new NullPointerException();        int mc = modCount;        // Android modified - breaks from the loop when modCount != mc        for (LinkedHashMapEntry<K,V> e = header.after; modCount == mc && e != header; e = e.after)            e.value = function.apply(e.key, e.value);        if (modCount != mc)            throw new ConcurrentModificationException();    }}

1、LinkedHashMap是如何实现LRU的。
首先,当accessOrder为true时,才会开启按访问顺序排序的模式,才能用来实现LRU算法。
我们可以看到,无论是put方法还是get方法,都会导致目标Entry成为最近访问的Entry,因此便把该Entry加入到了双向链表的末尾(get方法通过调用recordAccess方法来实现,put方法在覆盖已有key的情况下,也是通过调用recordAccess方法来实现,在插入新的Entry时,则是通过createEntry中的addBefore方法来实现),这样便把最近使用了的Entry放入到了双向链表的后面,多次操作后,双向链表前面的Entry便是最近没有使用的,这样当节点个数满的时候,删除的最前面的Entry(head后面的那个Entry)便是最近最少使用的Entry。

2、LinkedHashMap由于继承自HashMap,因此它具有HashMap的所有特性,同样允许key和value为null。

3、注意源码中的accessOrder标志位,
当它false时,表示双向链表中的元素按照Entry插入LinkedHashMap到中的先后顺序排序,即每次put到LinkedHashMap中的Entry都放在双向链表的尾部,这样遍历双向链表时,Entry的输出顺序便和插入的顺序一致,这也是默认的双向链表的存储顺序;
当它为true时,表示双向链表中的元素按照访问的先后顺序排列,可以看到,虽然Entry插入链表的顺序依然是按照其put到LinkedHashMap中的顺序,但put和get方法均有调用recordAccess方法(put方法在key相同,覆盖原有的Entry的情况下调用recordAccess方法),该方法判断accessOrder是否为true,如果是,则将当前访问的Entry(put进来的Entry或get出来的Entry)移到双向链表的尾部(key不相同时,put新Entry时,会调用addEntry,它会调用creatEntry,该方法同样将新插入的元素放入到双向链表的尾部,既符合插入的先后顺序,又符合访问的先后顺序,因为这时该Entry也被访问了),否则,什么也不做。

4、注意构造方法,前四个构造方法都将accessOrder设为false,说明默认是按照插入顺序排序的,而第五个构造方法可以自定义传入的accessOrder的值,因此可以指定双向循环链表中元素的排序规则,一般要用LinkedHashMap实现LRU算法,就要用该构造方法,将accessOrder置为true。

5、LinkedHashMap并没有覆写HashMap中的put方法,而是覆写了put方法中调用的addEntry方法和recordAccess方法。