美文网首页
LinkedHashMap

LinkedHashMap

作者: 抠脚大汗 | 来源:发表于2018-08-16 18:11 被阅读0次

单看该类名称。Linked链表+HashMap,不看类实现也能从字面猜出其大概功能。是把HashMap做了有序处理。即对传统的HashMap做了二次加工。这样,在对HashMap使用过程中又有对顺序有要求的情况下,可以直接使用LinkedHashMap,避免了开发者维护顺序的操作。该类的重点就是加入双向链表保证了顺序

public class LinkedHashMap<K,V> extends HashMap<K,V>  implements Map<K,V>

继承HashMap,实现Map接口

LinkedHashMap中存在的实例属性

//The head of the doubly linked list.
private transient LinkedHashMapEntry<K,V> header;

看注释:doubly linked list 双向链表 head表示双向链表的头
看声明:LinkedHashMapEntry类型,经验告诉我们这是一个内部类,是双向链表的Entry体,找LinkedHashMapEntry体的实现

private static class LinkedHashMapEntry<K,V> extends HashMapEntry<K,V>

真是LinkedHashMap的静态内部类,继承自HashMapEntry
构造方法直接调用的super

LinkedHashMapEntry(int hash, K key, V value, HashMapEntry<K,V> next) {
     super(hash, key, value, next);
}
// These fields comprise the doubly linked list used for iteration.
LinkedHashMapEntry<K,V> before, after;

Entry里的before,after是用来构成双向链表的基础,提供iteration遍历用,确保了put元素后的有序性,Entry里提供了remove(),addBefore()与recordAccess()方法,remove与addBefore操作都比较简单,remove从doublyLinkedList中remove掉该Entry;addBefore将该Entry对象插入到参数entry前面,不过多分析,下面看下recordAccess()方法

/**
 * 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);
       }
}

看方法注释有点慒,如果accessOrder为空,就一空方法。那来看下accessOrder属性与赋值的地方

/**
 * 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:插入顺序
定义成final了,通篇没有看到accessOrder为true的构造(只在构造里对其赋值了)
但有一个传进来的赋值

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

查看这个构造的创建,在LruCache与DiskBasedCache类里有调用,且传值都为true。
那继续分析recordAccess方法,在accessOrder为true时,方法才有意义,即访问顺序时才有意义,正好匹配了LruCache的初衷:Least recently used,最近最少使用,查看LinkedHashMap的get方法

public V get(Object key) {
        LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);
        if (e == null)
            return null;
        e.recordAccess(this);
        return e.value;
}

getEntry是父类HashMap方法,不介绍,获取entry对象后,如果有参数key对应的entry对象后,先执行了recordAccess方法。我们分析recordAccess方法,在访问顺序时,将该entry从链表中移除,并将entry插入到doublylinkedlist的header前。即通过LruCache的get方法保证常用的元素保持在链表头部。(如果为插入顺序的话,则recordAccess方法空跑)

这样LinkedHashMap,通过对LinkedHashMapEntry,增加了before与after属性,实现了双向链表的数据结构,复写了init方法(在父类HashMap中的构造里调用),将双向链表创建

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

在LinkedHashMap再调用put方法时,保证了存储的有序性

解释一下HashMap为啥无序。因为HashMap是用数组+链表的数据结构存储数据。为了解决hash冲突(解决到底存入数组的哪个链表中),做了h & (length-1);操作就是取模的优化写法。导致了put进数据的无序,数据元素中的单链表还是可以保持链表内的局部有序。但在大结构中无法保证有序。

LinkedHashMapEntry加入了before与after(独立于next属性),将数组元素内各链表重新做双向关联,确保了其有序性。在LinkedHashMap中加了accessOrder属性,在LrcCache类使用LinkedHashMap,传入accessOrder为true的参数,可以保证了最后访问的在链表的最前面。

PS:看的太粗陋,有时间的话还要再看两遍,三遍。并没有系统的看,愁进了方法,有错误,不妥的地方,还请各位看客即时指出。谢谢。

相关文章

网友评论

      本文标题:LinkedHashMap

      本文链接:https://www.haomeiwen.com/subject/xltobftx.html