LinkedHashMap源码解析

    public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
LinkedHashMap是HashMap的子类,实现了Map接口。
  • LinkedHashMap的属性
    private transient Entry<K,V> header;                //双向链表的头节点
    private final boolean accessOrder;                  //true:访问次序  false:插入次序
  • LinkedHashMap的静态内部类
    private static class Entry<K,V> extends HashMap.Entry<K,V> {                //基于HashMap的Entry实现
        Entry<K,V> before, after;                                               //一个before节点,一个after节点,双向链表

        Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
            super(hash, key, value, next);                                      //调用父类HashMap的构造方法
        }
        private void remove() {                                                 //删除当前节点
            before.after = after;                                               //将上个节点的after指向下个节点
            after.before = before;                                              //将下个节点的before指向上个节点
        }
        private void addBefore(Entry<K,V> existingEntry) {
            after  = existingEntry;                                             //当前节点的after指向给定的节点
            before = existingEntry.before;                                      //将当前节点的before指向给定节点的上一个节点
            before.after = this;                                                //上一个节点的after指向自己
            after.before = this;                                                //下一个节点的before指向自己
        }
        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方法,会回调该方法
            remove();
        }
    }
  • LinkedHashMap的属性构造方法
    public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);                         //构造一个指定初始容量和负载因子的LinkedList
        accessOrder = false;
    }
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);                                     //构造一个指定初始容量的LinkedList
        accessOrder = false;
    }
    public LinkedHashMap() {
        super();                                                    //用HashMap的初始化容量和负载因子创建一个LinkedHashMap
        accessOrder = false;
    }
    public LinkedHashMap(Map<? extends K, ? extends V> m) {
        super(m);                                                   //传入map创建一个LinkedHashMap
        accessOrder = false;
    }
    public LinkedHashMap(int initialCapacity,                       //根据指定初始容量和负载因子,键值对保持顺序来创建一个LinkedHashMap
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }
默认都用插入顺序来创建LindedHashMap,其header属性并没有看见在构造方法里初始化,在调用父类的构造方法中来初始化,看过HashMap源码的应该都知道构造方法里有个空的init方法,LinkedHashMap重写了init方法,来初始化header链表
    @Override
    void init() {
        header = new Entry<>(-1, null, null, null);                  //构造一个双向链表,类似linkedList
        header.before = header.after = header;
    }

put
LinkedHashMap本身没有put方法,调用put方法,是调用HashMap的put方法,但是在调用父类的put方法有子类重写的方法,会去调用子类重写的方法(java多态)

    @Override                                                           //重写了扩容方法  
    void transfer(HashMap.Entry[] newTable, boolean rehash) { //HashMap里面的transfer是n*m次运算,LinkedHashtable重写后是n+m次运算          
        int newCapacity = newTable.length;
        for (Entry<K,V> e = header.after; e != header; e = e.after) {       //直接遍历header链表,HashMap里面是遍历Entry数组
            if (rehash)
                e.hash = (e.key == null) ? 0 : hash(e.key);
            int index = indexFor(e.hash, newCapacity);
            e.next = newTable[index];
            newTable[index] = e;
        }
    }
     void addEntry(int hash, K key, V value, int bucketIndex) {
        super.addEntry(hash, key, value, bucketIndex);
        Entry<K,V> eldest = header.after;                                   //取出header后的第一个节点
        if (removeEldestEntry(eldest)) {
            removeEntryForKey(eldest.key);
        }
    }
    void recordAccess(HashMap<K,V> m) {                                     //super.addEntry时有个recordAccess方法,是空
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;                  //子类进行了重写
            if (lm.accessOrder) {
                lm.modCount++;                                              //先删除,再添加,相当于移动
                remove();                                                   //删除当前元素
                addBefore(lm.header);                                       //加入到链表尾部
            }
    }
    protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {            //默认返回false,如想实现LRU Cache,重写该方法
        return false;                                                       //即指定size大小,超过size大小返回true,删除不经常使用的元素
    }
    final Entry<K,V> removeEntryForKey(Object key) {                        //删除第一个节点
        if (size == 0) {
            return null;
        }
        int hash = (key == null) ? 0 : hash(key);
        int i = indexFor(hash, table.length);
        Entry<K,V> prev = table[i];
        Entry<K,V> e = prev;

        while (e != null) {
            Entry<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;
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }

        return e;
    }

get

    public V get(Object key) {
        Entry<K,V> e = (Entry<K,V>)getEntry(key);                       //复用了HashMap中的getEntry方法
        if (e == null)
            return null;
        e.recordAccess(this);                                           
        return e.value;
    }

remove
没有重写remove方法,即是调用父类HashMap的删除方法,父类有调用recordRemoval方法,在父类中是空的,子类有重写该方法,修正被删除节点的前后指向关系。

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

总结

LinkedHashMap 是 HashMap 的子类,其具有HashMap的所有特性,key和value都允许为空,key重复会覆盖,value可以重复,有序,非线程安全。
LinkedHashMap增加了两个属性,双向链表头结点header和标志位accessOrder用于保证顺序。
LRU是Least Recently Used 近期最少使用算法,LRU与LFU(Least Frequently Used)、FIFO(First In First Out)是3种常用的缓存算法(页面置换算法)。
继承LinkedHashMap,重写removeEldestEntry方法,accessOrder为true,实现LRU。

原文地址:https://www.cnblogs.com/Ch1nYK/p/8639373.html