zoukankan      html  css  js  c++  java
  • 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。

  • 相关阅读:
    react 组件间通信,父子间通信
    微信公众号配置及微信jsAPI支付
    Vue 幸运大转盘
    Hystrix断路器配置属性解析
    使用熔断器仪表盘监控(hystrix)
    使用熔断器防止服务雪崩
    创建服务消费者(Feign)
    Spring Cloud Ribbon配置详解
    创建服务消费者(Ribbon)
    创建服务提供者
  • 原文地址:https://www.cnblogs.com/Ch1nYK/p/8639373.html
Copyright © 2011-2022 走看看