LinkedHashMap是LruCache的基礎(chǔ),可以認(rèn)為,LinkedHashMap是不限容量的Lru,Lru是限制了容量的LinkedHashMap。
LinkedHashMap
LinkedHashMap擴(kuò)展自HashMap,而且數(shù)據(jù)存儲(chǔ)對(duì)象改為擴(kuò)展自HashMapEntry的LinkedHashMapEntry,主要是增加了before和after,實(shí)現(xiàn)雙向鏈表。
LinkedHashMap持有一個(gè)LinkedHashMapEntry類型的header,作為鏈表的表頭。
所以LinkedHashMap既有HashMap的數(shù)組+鏈表的二維存儲(chǔ)結(jié)構(gòu),又有鏈表的前后關(guān)系的查找結(jié)構(gòu)。
所以,清空數(shù)據(jù)集合時(shí),既要調(diào)用父類HashMap的數(shù)據(jù)清空,又要調(diào)用自身鏈表的前后關(guān)系清空(把header的before和after全都指向header自己)。
LinkedHashMap自帶部分LRU功能,構(gòu)造函數(shù):
public LinkedHashMap(int initialCapacity,
float loadFactor,
boolean accessOrder) {
super(initialCapacity, loadFactor);
this.accessOrder = accessOrder;
}
其中,第三個(gè)參數(shù)就代表是否開啟LRU,如果為true,在get時(shí),會(huì)挪到header前面,注意,header是不變的:
void recordAccess(HashMap<K,V> m) {
LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
if (lm.accessOrder) {
lm.modCount++;
remove();//從當(dāng)前鏈表節(jié)點(diǎn)挪走
addBefore(lm.header);//添加為新的header
}
}
所以,多次訪問后,鏈表的數(shù)據(jù)關(guān)系為:
-第一個(gè)訪問--第二個(gè)訪問--第三個(gè)訪問--header--最久沒有訪問-(這是一個(gè)雙向鏈表)
所以,header前面是最近訪問的,header后面是最久沒有訪問的。
因?yàn)楸闅v地時(shí)候會(huì)從header開始:
private abstract class LinkedHashIterator<T> implements Iterator<T> {
LinkedHashMapEntry<K,V> nextEntry = header.after;
所以,最前面取得的數(shù)據(jù)就是最久沒有訪問的數(shù)據(jù)。
LruCache
因?yàn)長(zhǎng)inkedHashMap可以根據(jù)最近訪問進(jìn)行排序,所以LruCache使用LinkedHashMap來存儲(chǔ)數(shù)據(jù):
public LruCache(int maxSize) {
...
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);//開啟LinkedHashMap的訪問排序
}
LinkeHashMap是可以自動(dòng)擴(kuò)容的,所以在LruCache中需要限制數(shù)據(jù)集合的上限,主要方法就是在put時(shí),擠走最久沒有訪問的數(shù)據(jù):
public final V put(K key, V value) {
...
trimToSize(maxSize);//擠走最久沒有訪問的數(shù)據(jù)
return previous;
}
其實(shí)就是從header頭開始遍歷LinkedHashMap,依次從最久沒有訪問的數(shù)據(jù)開始清理:
public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
...
if (size <= maxSize) {
break;
}
Map.Entry<K, V> toEvict = map.eldest();
if (toEvict == null) {
break;
}
...
map.remove(key);
...
}
entryRemoved(true, key, value, null);
}
}