继承自HahMap。

此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序)。注意,如果在映射中重新插入键,则插入顺序不受影响。(如果在调用 m.put(k, v) 前 m.containsKey(k) 返回了 true,则调用时会将键 k 重新插入到映射 m 中。)

提供特殊的构造方法来创建链接哈希映射,该哈希映射的迭代顺序就是最后访问其条目的顺序,从近期访问最少到近期访问最多的顺序(访问顺序)。这种映射很适合构建 LRU 缓存。调用 put 或 get 方法将会访问相应的条目(假定调用完成后它还存在)。putAll 方法以指定映射的条目集合迭代器提供的键-值映射关系的顺序,为指定映射的每个映射关系生成一个条目访问。任何其他方法均不生成条目访问。特别是,collection 视图上的操作不 影响底层映射的迭代顺序。

源码

LinkedHashMap中新定义的两个属性:

/**
     * The head of the doubly linked list.
     */
    private transient Entry<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
     */
    private final boolean accessOrder;//排序方法:true表示最近最少使用次序,false表示插入顺序

构造方法:(相较于HashMap,多了一个指定排序规则的方法,若不指定排序方法,默认按照插入顺序排序)

public LinkedHashMap(int initialCapacity, float loadFactor) {
       super(initialCapacity, loadFactor);
       accessOrder = false;
   }

   public LinkedHashMap(int initialCapacity) {
       super(initialCapacity);
       accessOrder = false;
   }  

   public LinkedHashMap() {
       super();
       accessOrder = false;
   }

   public LinkedHashMap(Map<? extends K, ? extends V> m) {
       super(m);
       accessOrder = false;
   }

   public LinkedHashMap(int initialCapacity,
                        float loadFactor,
                        boolean accessOrder) {
       super(initialCapacity, loadFactor);
       this.accessOrder = accessOrder;
   }

同时,LinkedHashMap中重写了init方法:(初始化了头结点)

@Override
    void init() {
        header = new Entry<>(-1, null, null, null);
        header.before = header.after = header;
    }

LinkedHashMap中Entry结构如下:

/**
     * LinkedHashMap entry.
     */
    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;
 
        Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
            super(hash, key, value, next);
        }
 
        /**
         * Removes this entry from the linked list.
         */
        private void remove() {
            before.after = after;
            after.before = before;
        }
 
        /**
         * Inserts this entry before the specified existing entry in the list.
         */
        private void addBefore(Entry<K,V> existingEntry) {
            // 将当前元素插入到指定元素之前(传入header节点时,相当于插入到链表尾部)
            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();
        }
    }

新增了before、After两个节点,用来维护Entry节点的顺序。

在插入元素的时候,会调用Entry中的addBefore方法,以此来维护插入顺序:(见上边Entry源码)

void addEntry(int hash, K key, V value, int bucketIndex) {
        super.addEntry(hash, key, value, bucketIndex);
 
        // Remove eldest entry if instructed
        Entry<K,V> eldest = header.after;
        if (removeEldestEntry(eldest)) {
            removeEntryForKey(eldest.key);
        }
    }
    void createEntry(int hash, K key, V value, int bucketIndex) {
        HashMap.Entry<K,V> old = table[bucketIndex];
        Entry<K,V> e = new Entry<>(hash, key, value, old);
        table[bucketIndex] = e;
        // 维护顺序,插入到末尾
        e.addBefore(header);
        size++;
    }
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        return false;
    }

若设置按访问顺序排序,则在读取元素时会把刚读取的元素放到双向链表的尾部:(见上边Entry源码)

public V get(Object key) {
        Entry<K,V> e = (Entry<K,V>)getEntry(key);
        if (e == null)
            return null;
        // 若使用访问顺序排序,则会把这个元素删除后插入到header之前
        e.recordAccess(this);
        return e.value;
    }

迭代器实现

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

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

    private class KeyIterator extends LinkedHashIterator<K> {
        public K next() { return nextEntry().getKey(); }
    }

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

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

应用

LinkedHashMap可以实现一个采用FIFO替换策略的缓存(LRUCache即Least Recently Used Cache,最近最少使用),如下:

class FIFOCache<K, V> extends LinkedHashMap<K, V>{
    private final int cacheSize;
    public FIFOCache(int cacheSize){
        this.cacheSize = cacheSize;
    }
 
    // 当Entry个数超过cacheSize时,删除最老的Entry
    @Override
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
       return size() > cacheSize;
    }
}

在插入元素的时候,LinkedHashMap会调用removeEldestEntry方法,LinkedHashMap默认会返回false,若子类重写了该方法返回true,则会删除掉header.befor元素,即双向链表最后的一个元素。(见前边源代码)

总结

LinkedHashMap继承自HashMap,相较于HashMap,其内部通过一个双向链表来维护节点的顺序,默认顺序为插入顺序,新插入的元素会插到链表尾部,若accessOrder为true,则会以访问顺序来进行排序,在每次get元素时将访问的元素放到链表尾部。