LruCache
public class LruCache<K, V> {
//hashmap初始容量100率寡,實際會初始化一個128長度的table
//accessOrder:true,按訪問順序排序
private final java.util.LinkedHashMap<K, V> cache =
new LinkedHashMap<K, V>(100, 0.75f, true);
//目前cache最大容量
private int maxSize;
//最開始的cache最大容量
private final int initialMaxSize;
//cache當前大小
private int currentSize = 0;
/**
* @param size cache最大的size,單位應該和getSize(Object)放回得到的size的單位一致十气。比如字節(jié)捕捂。
*/
public LruCache(int size) {
this.initialMaxSize = size;
this.maxSize = size;
}
/**
* 設置一個乘數(shù)瑟枫,作用于size斗搞,如果一開始的時候我們設置的maxSize = 100,通過這個方法設置一個乘數(shù),
* 比如設置的是2慷妙,那新的maxSize就是200僻焚,如果設置的乘數(shù)是0.5,那maxsize就是50
* 如果計算出來的新的maxSize < currentSize膝擂,entries就執(zhí)行收縮虑啤,驅(qū)逐年齡最大的,直到當前的size <= 新的 maxSize
*/
public void setSizeMultiplier(float multiplier) {
if (multiplier < 0) {
throw new IllegalArgumentException("Multiplier must be >= 0");
}
//計算一個新的maxSize
maxSize = Math.round(initialMaxSize * multiplier);
//試圖驅(qū)逐年齡最大的node
evict();
}
/**
* 返回緩存對象的item的size,默認這里設置的是1架馋,如果是bitmap狞山,需要子類重寫這個方法,然后計算item真正的size返回叉寂。
* 計算的單位需要和構(gòu)造方法中的size匹配萍启,單位通常是字節(jié)。
*
* @param item 要計算緩存大小的對象屏鳍。
*/
protected int getSize(V item) {
return 1;
}
/**
* 如果一個item從cache中被驅(qū)逐勘纯,這個回調(diào)方法需要在合適的時機被調(diào)用,默認是個空實現(xiàn)钓瞭,需要子類重寫驳遵。
*
* @param key 被驅(qū)逐的item的key.
* @param item 被驅(qū)逐的item.
*/
protected void onItemEvicted(K key, V item) {
// optional override
}
public int getMaxSize() {
return maxSize;
}
public int getCurrentSize() {
return currentSize;
}
/**
* 直接調(diào)用HashMap的方法,判斷與key相同的緩存單元是否存在山涡。
*/
public boolean contains(K key) {
return cache.containsKey(key);
}
/**
* 根據(jù)key獲取item堤结,直接調(diào)用的是LinkedHashMap 的方法。
*
* @param key The key to check.
*/
public V get(K key) {
return cache.get(key);
}
/**
* Adds the given item to the cache with the given key and returns any previous entry for the given key that may
* have already been in the cache.
*
* 把一個item添加到緩存佳鳖,如果之前已經(jīng)有添加過相同的key的緩存霍殴,那hashmap默認其實會將value替換,返回返回之前的value系吩。
* 所以這個方法的返回值就是那個替換下來的舊的value。如果key沒有添加過妒蔚,那么會返回null穿挨。
*
* 如果, 要添加的這個item的單個的緩存 > 整個緩存的maxSize。那就不能讓這個item添加成功肴盏,直接舍棄科盛,并且調(diào)用onItemEvicted回調(diào)。
*/
public V put(K key, V item) {
final int itemSize = getSize(item);
if (itemSize >= maxSize) {
onItemEvicted(key, item);
return null;
}
final V result = cache.put(key, item);
if (item != null) {
currentSize += getSize(item);
}
if (result != null) {
// TODO: should we call onItemEvicted here?
currentSize -= getSize(result);
}
evict();
return result;
}
/**
* 刪除
*/
public V remove(K key) {
//根據(jù)key刪除value
final V value = cache.remove(key);
if (value != null) {
//如果value占據(jù)著緩存容量菜皂,從currentSize中減掉item的緩存size贞绵。
currentSize -= getSize(value);
}
return value;
}
/**
* 清空
*/
public void clearMemory() {
trimToSize(0);
}
/**
* 如果currentSize > size, 清除緩存中哪些年齡最大的緩存單元,直到cache滿足currentSize <= size
*/
protected void trimToSize(int size) {
Map.Entry<K, V> last;
while (currentSize > size) {
//從表頭取數(shù)據(jù)
last = cache.entrySet().iterator().next();
//獲取緩存數(shù)據(jù)
final V toRemove = last.getValue();
//計算這個緩存item的size恍飘,并計算當前currentSize
currentSize -= getSize(toRemove);
//得到這個item的key
final K key = last.getKey();
//從緩存總刪除
cache.remove(key);
//調(diào)用callBack
onItemEvicted(key, toRemove);
}
}
//驅(qū)逐年齡大的緩存單元榨崩。直到maxSize
private void evict() {
trimToSize(maxSize);
}