介紹
一個快取類,持有快取物件的強引用。每當使用到一個快取的value,會把它放到資料容器的頂部。當快取存滿,資料容器尾部的快取value被驅逐。
注意點
- 如果你的快取物件需要顯示釋放,重寫entryRemoved
- 如果讀取快取發生未命中而且需要為此次讀取生成一個物件返回,需要重寫create。這樣的話,使用者可以認為即使快取未命中,夜總會返回一個value。
- 預設,快取的size是計算的entry的個數。你可以通過重寫sizeOf實現快取的size是每個entery對應value的大小相加。
- LruCache不予許null作為key或者value。當出現return為null,代表沒有這個key對應的value
- LruCache提供的操作是執行緒安全的。
閱讀
LinkedHashMap是Lrucache的核心資料容器,LruCache最近最少使用策略也是通過LinedHashMap天然實現。我們這裡不去談LinkedHashMap的儲存,只看LurCache的初始化,get/put/remove操作。
初始化
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
複製程式碼
只是初始化maxsize欄位,沒做什麼特別的事情。
get
public final V get(K key) {
// 驗證key不能為null
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
//進行讀取動作,通過synchronized保證併發
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
// 未命中時,create一個value。若未重寫create,返回null
// 這個方法可能發生併發操作。若發生併發,永遠拋棄createValue
V createdValue = create(key);
if (createdValue == null) {
return null;
}
// 可以發現,任何涉及對linkedhashmap進行操作的時候,
// 需要進行synchronization處理
synchronized (this) {
createCount++;
mapValue = map.put(key, createdValue);
if (mapValue != null) {
// 這裡就是上面說的,併發的時候,拋棄createValue
map.put(key, mapValue);
} else {
size += safeSizeOf(key, createdValue);
}
}
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}
複製程式碼
put
public final V put(K key, V value) {
// 不支援key value 為null。
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
putCount++;
// 不管三七二十一先加入linkedhashmap,最後判斷是否溢位
size += safeSizeOf(key, value);
previous = map.put(key, value);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, value);
}
// 這裡判斷溢位,回收。
trimToSize(maxSize);
return previous;
}
複製程式碼
remove
public final V remove(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V previous;
synchronized (this) {
previous = map.remove(key);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, null);
}
return previous;
}
複製程式碼
trimToSize 最少演算法在這裡被涉及到
**public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
// linkedhashmap的eldest就提供了最少使用的entry。
Map.Entry<K, V> toEvict = map.eldest();
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}**
複製程式碼
總結
看了這幾個操作,是否感覺空空?是的,這幾個操作也就是注意synchronization,固定的entryRmove通知,再無其他。而最少使用的實現,看LinkedHashMap。