LruCache源碼的理解
使用場景
在Android手機上加載圖片,一般會用到三級緩存策略
內存的緩存策略,一般會用到LruCache來解決
內存用於緩存遇到的問題
1. 手機給每個應用分配的內存空間是有限的,如果內存中數據量過大,會造成OOM,所以,我們需要適當的清理內存
2. 在清理內存的時候,我們希望最老的部分最先被清除,從而讓出空間來
LruCache中使用LinkedHashMap可以解決的問題
1. LinkedHashMap可以當做一個容器來使用
2. LinkedHashMap是一個Map,可以用key來給數據做標記
3. LinkedHashMap支持訪問順序,每次get()或者add()一個數據,都會把相應的數據放到鏈表的尾部,這樣,鏈表頭部的數據就是沒怎麼用過的數據,也就是最老的數據
圖形解釋
RTFRS解釋
public class LruCache<K, V> {
//正真用於緩存的容器
//支持訪問順序呢:指在迭代遍歷列表中的元素時最近訪問的元素會排在LinkedHashMap的尾部
//清除操作是從頭部開始清除的,可以實現“最近最少使用”的需求
private final LinkedHashMap<K, V> map;
//當前換粗的大小
private int size;
//最大容量
private int maxSize;
//put進來的數量
private int putCount;
//create出來的數量
private int createCount;
//清除的數量
private int evictionCount;
//get到值的數量
private int hitCount;
//沒有get到值得數量
private int missCount;
//構造方法
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);
}
//重新設置最大容量
public void resize(int maxSize) {
//異常處理
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
//更新最大容量
synchronized (this) {
this.maxSize = maxSize;
}
//size的值可能變小,嘗試清除部分緩存
trimToSize(maxSize);
}
//通過key來獲取相應的值
public final V get(K key) {
//異常處理
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
//獲取到相應的對象
mapValue = map.get(key);
if (mapValue != null) {
//當容器中儲存了相應的值,且不爲空,那麼:更新hitCount的數據
hitCount++;
return mapValue;
}
//當容器中沒有儲存相應的值,那麼:更新missCount的數據
missCount++;
}
//create一個值,默認爲null
V createdValue = create(key);
if (createdValue == null) {
return null;
}
//重寫create()方法後執行下面的代碼
synchronized (this) {
//create出了一個值,那麼:更新createCount的數據
createCount++;
//放入容器,map.put()的返回值:
//1.null,新添加的數據
//2.不爲null,表示鏈表中存在相應的value,put()表示更新數據,將舊的數據返回
mapValue = map.put(key, createdValue);
//對mapValue的判斷表示put()的操作是更新還是添加
if (mapValue != null) {
//更新操作,那麼create出來的值不能覆蓋以前的值
//將舊的值重新放回到容器中
map.put(key, mapValue);
} else {
//添加進來新的值,那麼容器的大小會發生變化,更新size的值
size += safeSizeOf(key, createdValue);
}
}
if (mapValue != null) {
//默認什麼都沒有做
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
//size變大,嘗試清除部分緩存
trimToSize(maxSize);
return createdValue;
}
}
//添加到緩存中
public final V put(K key, V value) {
//異常的處理
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
//有新的對象添加到緩存中:那麼,更新putCount的數據
putCount++;
//size變化,更新size的值
size += safeSizeOf(key, value);
//添加到容器中
previous = map.put(key, value);
//跟get()中的原理一樣,這裏判斷是更新還是添加
if (previous != null) {
//更新操作,那麼size多記錄一份舊的value的大小,減去相應的值
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
//默認什麼都沒有做
entryRemoved(false, key, previous, value);
}
//size可能變大,嘗試清除部分緩存
trimToSize(maxSize);
return previous;
}
//嘗試清除部分緩存
public void trimToSize(int maxSize) {
//無限循環,與if (size <= maxSize)配合:可以將緩存清空到maxSize以下
while (true) {
K key;
V value;
synchronized (this) {
//異常處理
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
//當前size沒有達到maxSize,結束循環
if (size <= maxSize) {
break;
}
//獲取鏈表的第一個條目
Map.Entry<K, V> toEvict = map.eldest();
//沒有緩存下數據,結束循環
if (toEvict == null) {
break;
}
//移除第一個條目
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
//更新size的數據
size -= safeSizeOf(key, value);
//這裏執行了一次清除,那麼,更新evictionCount的值
evictionCount++;
}
//默認什麼都沒有做
entryRemoved(true, key, value, null);
}
}
//移除相應的緩存
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的數據
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
//默認什麼都沒有做
entryRemoved(false, key, previous, null);
}
return previous;
}
//默認什麼都沒有做;如果有相應的需求,可以重寫該方法
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
//create出null;如果有相應的需求,可以重寫該方法
protected V create(K key) {
return null;
}
//判斷大小
private int safeSizeOf(K key, V value) {
int result = sizeOf(key, value);
if (result < 0) {
throw new IllegalStateException("Negative size: " + key + "=" + value);
}
return result;
}
//開放人員需要重寫的方法,用來標記每個item對應的大小
protected int sizeOf(K key, V value) {
return 1;
}
//獲取各種成員變量的方法
//...
//這裏獲取的是一個map的副本
public synchronized final Map<K, V> snapshot() {
return new LinkedHashMap<K, V>(map);
}
//不解釋
@Override public synchronized final String toString() {
int accesses = hitCount + missCount;
int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;
return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",
maxSize, hitCount, missCount, hitPercent);
}
}
結語
通過對LruCache的理解,實際上,我們是更加了解了冷門的LinkedHashMap的一些功能:可以記錄訪問順序。
轉載請標明出處:http://blog.csdn.net/qq_26411333/article/details/51523790