final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
……
/*
針對于key是null的重寫了newNode
*/
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
……
/*
在連結清單中找到了key相同的值進行替換時,激活通路模式的回調
*/
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
/**
激活LRU模式的回調
*/
afterNodeInsertion(evict);
return null;
}
/**
* 這是一個生命周期方法,為了實作擴充
* 原有的是空實作,LinkedHashMap重寫了這些,我們下面逐個講解這些方法
*/
void afterNodeAccess(Node<K,V> p) { }//通路模式專用的連結清單調整
void afterNodeInsertion(boolean evict) { }//LRU模式專用的連結清單調整
void afterNodeRemoval(Node<K,V> p) { }//删除時的的連結清單調整
LinkedHashMap——周遊
public final void forEach(Consumer<? super K> action) {
if (action == null)
throw new NullPointerException();
int mc = modCount;
/*
周遊起始條件是:e首先=head;
周遊終止條件是:e==null;
周遊自增條件是:e.after,選擇下一個;
*/
for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
action.accept(e.key);
if (modCount != mc)
throw new ConcurrentModificationException();
}
final boolean accessOrder;
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
/*
當元素被通路到後就會進行一次重新的before和after編排
*/
if (accessOrder)
afterNodeAccess(e);
return e.value;
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
……
/*
在連結清單中找到了key相同的值進行替換時
進行一次重新的before和after編排
*/
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
return null;
}
/**
* @滞末處理
* 更改節點至尾部,顯然要處理之前節點把斷層補上
*/
void (Node<K,V> e) { // move node to last
LinkedHashMap.Entry<K,V> last;
/**
* @條件開啟了accessOrder通路順序模式且如果舊節點不是末尾節點才進入滞末處理
* last指向末尾節點
*/
if (accessOrder && (last = tail) != e) {
/**
* e代表舊的剛被替換的節點,p是強轉後的e,相容hashMap的方法原型
* b代表被替換的前繼,a代表被替換的後繼
*/
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after = null;//即将被調整後最後,是以最後肯定是null但是指針已經在前面保留了
/**
* @去除舊節點連接配接兩個節點中間的斷層
*/
/**
* @(1)舊節點是頭節點(沒前繼,前繼是空)
* 直接頭結點指向自己的後繼
*/
if (b == null)
head = a;
else //否則舊節點的前繼節點向後連接配接舊節點的後繼節點
b.after = a;
/**
* @(2)舊節點是中間節點(後有繼不是空)
* 直接舊節點的後繼向前連接配接舊節點的前繼,剛好上面相反
*/
if (a != null)
a.before = b;
else //否則曾經是最後一個節點,将last指向前繼,繼續處理
last = b;
/**
* @(3)處理隻有一個節點時,last也是空(last指向前繼是空或者tail是空)
*/
if (last == null)
head = p;
else {
/**
* 這裡算是一個兜底政策,可能有兩個情況
* 自己是中間節點,last就是tali,做一下指針調整
* 自己是最後一個節點,last是節點的前繼節點,就是上面那個情況,曾經是最後一個節點,是以前繼就是倒數二個,做一下這兩個的指針調整
*/
p.before = last;
last.after = p;
}
tail = p;//調整整體指針
++modCount;
}
}
void afterNodeInsertion(boolean evict) { // possibly remove eldest
LinkedHashMap.Entry<K,V> first;
/**
* @此次實作LRU算法
* 需要子類繼承LinkHashMap實作removeEldestEntry方法,該方法傳入頭結點以及某種邏輯決定
* 是否移除頭節點,因為頭節點意味着經常不被通路
*/
if (evict && (first = head) != null && removeEldestEntry(first)) {
K key = first.key;
removeNode(hash(key), key, null, false, true);
}
}
LinkedHashMap——LRU算法實作
在插入的時候有這麼一個回調處理,
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
……
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}