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。