单看该类名称。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:看的太粗陋,有时间的话还要再看两遍,三遍。并没有系统的看,愁进了方法,有错误,不妥的地方,还请各位看客即时指出。谢谢。