【Java原始碼分析】Android-LruCache原始碼分析
內部實現是LinkedHashMap,保持有限數量的值得強引用,值被訪問之後就被移動到佇列的首部。當佇列滿了之後,尾部的值會被移除以便於GC回收
類的定義
public class LruCache<K, V> {}
- 如果被快取的值所擁有的職員需要被顯式的釋放,過載entryRemoved()方法
- 預設情況下size方法返回的是條目數,如果需要返回快取大小,可以過載sizeOf()
- 執行緒安全的類
- 不允許NULL的key,因此get put remove返回null的時候是沒有二義性的
構造方法
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); }
建立一個LruCache,如果沒有過載sizeOf()那麼maxSize是最大條目數,如果過載了,就是用於過載所用單位
取值方法
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++; return mapValue; } missCount++; } /* * Attempt to create a value. This may take a long time, and the map * may be different when create() returns. If a conflicting value was * added to the map while create() was working, we leave that value in * the map and release the created value. */ V createdValue = create(key); if (createdValue == null) { return null; } synchronized (this) { createCount++; mapValue = map.put(key, createdValue); if (mapValue != null) { // There was a conflict so undo that last put map.put(key, mapValue); } else { size += safeSizeOf(key, createdValue); } } if (mapValue != null) { entryRemoved(false, key, createdValue, mapValue); return mapValue; } else { trimToSize(maxSize); return createdValue; } }
如果對應的key存在於快取或者可以被建立,那麼返回key對應的value。否則返回null.
如果在建立過程中出現了值得衝突,那麼就丟棄當前建立的value,保留之前的。
存值方法
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++; 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; }
將當前的鍵值對存入Map,並把當前鍵值對移到佇列最前方,返回該key之前對映的值
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
鍵值對被移除時呼叫,預設是空實現。方法是沒有實現同步的,所以在執行時可能被其他執行緒訪問。如果第一個引數是true,就代表是移除以空出空間。如果是false代表是被put或者remove呼叫。最後一個引數是key對應的新值,如果非空,就說明是被put呼叫,如果為空,說明是被eviction或者remove
移除最久的鍵值對
public void trimToSize(int 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!");
}
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 -= safeSizeOf(key, value);
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 -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, null);
}
return previous;
}
刪除key所對映的之前的值,返回值是key先前的對映值
快取未命中時計算給定key的相關值時被呼叫的方法,在get中被使用(當get的時候當前key沒有對應的鍵值對,就需要呼叫create()建立)這個方法需要和get結合來看
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;
}
protected int sizeOf(K key, V value) {
return 1;
}
返回給定引數鍵值對應實體的大小,該大小是使用者定義的單位大小比如個數或者KB。預設返回1代表個數
清空快取
public final void evictAll() {
trimToSize(-1); // -1 will evict 0-sized elements
}
對每一個entry呼叫entryRemoved
獲取快取大小
public synchronized final int size() {
return size;
}
對於沒有覆寫sizeOf()的時候返回的是快取中實體的最大個數,如果覆寫了sizeOf()返回的是所有實體大小之和的最大值
快取命中數
public synchronized final int hitCount() {
return hitCount;
}
public synchronized final int missCount() {
return missCount;
}
get方法命中次數和未命中數
返回快照
// ordered from least recently accessed to most recently accessed.
public synchronized final Map<K, V> snapshot() {
return new LinkedHashMap<K, V>(map);
}
返回當前快取的一個副本