深入学习java集合:LinkedHashMap实现

时间:2023-02-26 20:52:44
1、LinkedHashMap类图 深入学习java集合:LinkedHashMap<K,V>实现
深入学习java集合:LinkedHashMap<K,V>实现           LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
          LinkedHashMap实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。
          注意,此实现不是同步的,如果多个线程同时访问链接的哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。
          对于LinkedHashMap而言,它继承与HashMap、底层使用哈希表与双向链表来保存所有元素。其基本操作与父类HashMap相似,它通过重写父类相关的方法,来实现自己的链接列表特性。           利用LinkedHashMap可以实现LRU缓存, LinkedHashMap有两大好处:一是它本身已经实现了按照访问顺序的存储,也就是说,最近读取的会放在最前面,最最不常读取的会放在最后(当然,它也可以实现按照插入顺序存储)。第二,LinkedHashMap本身提供一个方法removeEldestEntry(eldest: Map.Entry<K, V>): boolean用于判断是否需要移除最不常读取的数,但是,原始方法默认不需要移除(这是,LinkedHashMap相当于一个linkedlist),所以,我们需要override这样一个方法,使得当缓存里存放的数据个数超过规定个数后,就把最不常用的移除掉。 2、 LinkedHashMap构造实现以及重要方法       LinkedHashMap底层实现如图所示:   深入学习java集合:LinkedHashMap<K,V>实现

  1) 构造器          LinkedHashMap提供了5个不同的构造器 //构造一个初始大小为initialCapacity,装载因子为loadFactor的空的LinkedHashMap      public LinkedHashMap(int initialCapacity, float loadFactor) {         super(initialCapacity, loadFactor);         accessOrder = false;  //默认链表顺序为插入程序,accessOrder 设置为false     } // 构造一个初始大小为initialCapacity,装载因子为默认的0.75的空的LinkedHashMap public LinkedHashMap(int initialCapacity) {         super(initialCapacity);         accessOrder = false;     } //构造一个初始大小为initialCapacity,装载因子为默认的loadFactor的空的LinkedHashMap,链表顺序根据accessOrder确定  public LinkedHashMap(int initialCapacity,                          float loadFactor,                          boolean accessOrder) {         super(initialCapacity, loadFactor);         this.accessOrder = accessOrder;     } // 在调用super()构造函数,也就是HashMap的相应构造函 数时,会调用init()方法,,该方法被重写,用于初始化header。 @Override     void init() {         header = new Entry<>(-1, null, null, null);         header.before = header.after = header;     }

2)   Entry元素            LinkedHashMap采用的hash算法和HashMap相同,但是它重新定义了数组中保存的元素Entry,该Entry除了保存当前对象的引用外,还保存了其上一个元素before和下一个元素after的引用,从而在哈希表的基础上又构成了双向链接列表。
      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);        }}3) get(key: Object): V  获取指定Key对应的Value方法     LinkedHashMap重写了父类HashMap的get方法,实际在调用父类getEntry()方法取得查找的元素后,再判断当排序模式accessOrder为true时,记录访问顺序,将最新访问的元素添加到双向链表的表头,并从原来的位置删除。由于的链表的增加、删除操作是常量级的,故并不会带来性能的损失。    public V get(Object key) {        Entry<K,V> e = (Entry<K,V>)getEntry(key);        if (e == null)            return null;        e.recordAccess(this);        return e.value;    } void recordAccess(HashMap<K,V> m) {            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;            if (lm.accessOrder) {   //如果是按照获取顺序排序,则移除,然后再添加到链表前端                lm.modCount++;                remove();                addBefore(lm.header);            }        }4) addEntry(hash: int, key: K, value: V, bucketIndex: int): void   向LinkedHashMap中添加元素方法          LinkedHashMap并未重写父类HashMap的put方法,而是重写了父类HashMap的put方法调用的子方法void addEntry(int hash, K key, V value, int bucketIndex) 和void createEntry(int hash, K key, V value, int bucketIndex),提供了自己特有的双向链接列表的实现。
      void addEntry(int hash, K key, V value, int bucketIndex) {        super.addEntry(hash, key, value, bucketIndex);  //调用父类HashMap的addEntry()方法        // Remove eldest entry if instructed        Entry<K,V> eldest = header.after;        if (removeEldestEntry(eldest)) {        //如果可能的话,移除最老的元素            removeEntryForKey(eldest.key);        }    }5) removeEldestEntry(eldest: Map.Entry<K, V>): boolean    重写该方法,可实现移除元素的方法          LinkedHashMap提供了removeEldestEntry(Map.Entry<K,V> eldest)方法,在将新条目插入到映射后,put和 putAll将调用此方法。该方法可以提供在每次添加新条目时移除最旧条目的实现程序,默认返回false,这样,此映射的行为将类似于正常映射,即永远不移除最旧的元素。      protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {        return false;    }
通过重写removeEldestEntry 方法,实现Lru缓冲。在方法中判断,如果当前容量大于设定的缓冲大小将返回true,将最老的元素移除。注意:实现lru缓冲时,将accessOrder设为true。  @Override  protected boolean removeEldestEntry (Map.Entry eldest) {               return size() > LRUCache.cacheSize;    }