本文只是学习笔记,如有错误,欢迎指出
LinkedHashMap继承HashMap,在HashMap的基础上维护一个Entry双向链表
HashMap的分析可以看上一篇
class LinkedHashMap<K,V> extends HashMap<K,V>
header是双向链表的头节点,header的after指向链表的最后一个节点,before指向第一个节点,
所以添加节点时,是添加到header的前面
private transient Entry<K,V> header;
accessOrder可以指定访问节点的时候,是否需要将被访问的节点移动到第一个节点位置
private final boolean accessOrder;
重载HashMap的init方法,这个方法在父类的构造器里调用
void init() {
//创建header节点
header = new Entry<K,V>(-1, null, null, null);
//header的before和after都指向自己
header.before = header.after = header;
}
Entry类,双向列表的节点,继承于HashMap.Entry
private static class Entry<K,V> extends HashMap.Entry<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
super(hash, key, value, next);
}
//删除节点方法,将改节点从双向链表中断开
//将前一个节点的after指向该节点的after
//将后一个节点的before指向该节点的before
private void remove() {
before.after = after;
after.before = before;
}
//将节点添加到existingEntry节点前面
private void addBefore(Entry<K,V> existingEntry) {
//after指向existingEntry
after = existingEntry;
//before指向existingEntry的前一个
before = existingEntry.before;
//existingEntry的前一个的after指向自己
before.after = this;
//将existingEntry的before指向自己
after.before = this;
}
//当节点被访问时(例如get方法)会调用这个方法
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {//如果accessOrder为true
lm.modCount++;
remove();//将自己从双向列表中断开
addBefore(lm.header);//将自己添加到header节点前面,也就是双向链表的第一个位置
}
}
//当节点被删除时调用这个方法
void recordRemoval(HashMap<K,V> m) {
//将自己从链表中断开
remove();
}
}
当调用put方法时,会调用HashMap的put方法
//HashMap的put方法
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);//被子类重写了
return null;
}
然后重写了addEntry方法,做一些LinkerHashMap自己的操作
void addEntry(int hash, K key, V value, int bucketIndex) {
//创建一个节点
createEntry(hash, key, value, bucketIndex);
//获取链表最后一个节点,就就是header.after所指的那个节点
Entry<K,V> eldest = header.after;
//如果需要,可以删除最老的节点
if (removeEldestEntry(eldest)) {
removeEntryForKey(eldest.key);
} else {
//如果大小超过阈值则扩容
if (size >= threshold)
resize(2 * table.length);
}
}
创建节点
void createEntry(int hash, K key, V value, int bucketIndex) {
//和HashMap的创建节点一样,放到对应的table桶上
HashMap.Entry<K,V> old = table[bucketIndex];
Entry<K,V> e = new Entry<K,V>(hash, key, value, old);
table[bucketIndex] = e;
//将新创建的 节点添加到链表第一个位置
e.addBefore(header);
size++;
}
判断是否需要删除最老的节点,也就是最少访问到的节点,默认是false
可以和accessOrder设置为true和重写这个方法来实现最近最少使用缓存(LRU)
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
get操作,如果有必要会移动到header的before位置(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;
}
删除操作,使用的是HashMap的remove方法
//HashMap的remove方法
public V remove(Object key) {
Entry<K,V> e = removeEntryForKey(key);
return (e == null ? null : e.value);
}
final Entry<K,V> removeEntryForKey(Object key) {
int hash = (key == null) ? 0 : hash(key.hashCode());
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;
//LinkedHashMap的Entry重写了这个方法
//被删除时,会从链表中删除
e.recordRemoval(this);
return e;
}
prev = e;
e = next;
}
return e;
}
resize操作,使用的是HashMap的resize
resize时会调用这个方法,LinkerHashMap重写了这个方法,因为用链表访问所以节点,性能更好
void transfer(HashMap.Entry[] newTable) {
int newCapacity = newTable.length;
//从链表的最后一个节点开始,遍历到第一个节点
for (Entry<K,V> e = header.after; e != header; e = e.after) {
//找到节点所在的桶位置
int index = indexFor(e.hash, newCapacity);
//将当前桶的链表指向该节点的next
e.next = newTable[index];
//将节点放到桶上
newTable[index] = e;
}
}