在之前讲LinkedHashMap的时候,我们说起可以用来实现LRU(least recent used)算法,接下来我看一下其中的一个具体实现-----android sdk 中的LruCache.
talk is cheap, I am gonna show you something really expensive.
package android.util;// 该类是从Android sdk 中摘录
public class LruCache {
private final LinkedHashMap map;// 这里的 LinkedHashMap 为 android.jar 中的类,与jdk中的有所区别,这里不做区分
private int size;// 当前 cache 的大小
private int maxSize;// cache 最大容量
private int putCount;// put() 调用的次数
private int createCount;// get()未命中,成功构建新 key:value 的次数
private int evictionCount;// 因cache大小超过容量限制,将 key:value 从 cache 中驱逐的次数
private int hitCount;// get()命中的次数
private int missCount;// get()未命中的次数
// 构造时设置最大容量
public LruCache(int maxSize) {
if (maxSize <&#61; 0) {
throw new IllegalArgumentException("maxSize <&#61; 0");
}
this.maxSize &#61; maxSize;
// 这里传入的 LinkedHashMap 的 accessOrder 为true,表示 其中的链表中的结点顺序为 "按访问顺序"
this.map &#61; new LinkedHashMap(0, 0.75f, true);
}
// 重新设置 cache 最大容量
public void resize(int maxSize) {
if (maxSize <&#61; 0) {
throw new IllegalArgumentException("maxSize <&#61; 0");
}
synchronized (this) {
this.maxSize &#61; maxSize;
}
trimToSize(maxSize);
}
public final V get(K key) {
if (key &#61;&#61; null) {
throw new NullPointerException("key &#61;&#61; null");
}
V mapValue;
synchronized (this) {// 同步访问
mapValue &#61; map.get(key);
if (mapValue !&#61; null) {
hitCount&#43;&#43;;// 命中次数 &#43; 1
return mapValue;
}
missCount&#43;&#43;;// 未命中次数 &#43; 1
}
V createdValue &#61; create(key);// 通过 key 构建一个value(比如从文件中读入value)
if (createdValue &#61;&#61; null) {// 创建失败
return null;
}
synchronized (this) {
createCount&#43;&#43;;// 构建 value 成功次数 &#43; 1
mapValue &#61; map.put(key, createdValue);// 添加到 LinkedHashMap 中
if (mapValue !&#61; null) {
// LinkedHashMap 原来有该key的值(在构建value的时候,被其他线程添加进去的),这里重新把原来的值放进去,cache大小没有增加
map.put(key, mapValue);
} else {
size &#43;&#61; safeSizeOf(key, createdValue);// cache 大小增加
}
}
if (mapValue !&#61; null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);// 大小增加了,保证在最大容量范围内
return createdValue;
}
}
public final V put(K key, V value) {
if (key &#61;&#61; null || value &#61;&#61; null) {
throw new NullPointerException("key &#61;&#61; null || value &#61;&#61; null");
}
V previous;
synchronized (this) {
putCount&#43;&#43;;// put 次数
size &#43;&#61; safeSizeOf(key, value);
previous &#61; map.put(key, value);
if (previous !&#61; null) {
size -&#61; safeSizeOf(key, previous);// 替换已有的value
}
}
if (previous !&#61; null) {
entryRemoved(false, key, previous, value);
}
trimToSize(maxSize);
return previous;
}
// 移除LRU键值对,保证 cache 的大小在 maxSize 范围内
public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
if (size <0 || (map.isEmpty() && size !&#61; 0)) {
throw new IllegalStateException(getClass().getName()
&#43; ".sizeOf() is reporting inconsistent results!");
}
if (size <&#61; maxSize) {
break;
}
Map.Entry toEvict &#61; map.eldest();// 最老的结点,返回head结点,也即 LRU结点
if (toEvict &#61;&#61; null) {// 没有可以删除的key:value了
break;
}
key &#61; toEvict.getKey();
value &#61; toEvict.getValue();
map.remove(key);
size -&#61; safeSizeOf(key, value);// 大小降低
evictionCount&#43;&#43;;// 驱逐次数 &#43; 1
}
entryRemoved(true, key, value, null);
}
}
public final V remove(K key) {
if (key &#61;&#61; null) {
throw new NullPointerException("key &#61;&#61; null");
}
V previous;
synchronized (this) {
previous &#61; map.remove(key);// 通过 LinkedHashMap 移除
if (previous !&#61; null) {
size -&#61; safeSizeOf(key, previous);// cache总大小降低
}
}
if (previous !&#61; null) {
entryRemoved(false, key, previous, null);
}
return previous;
}
// key:oldValue 被 移除 或 驱逐 时回调
protected void entryRemoved(boolean evicted/*是否是被驱逐(因cache大小超过容量限制被删除)*/,
K key, V oldValue, V newValue) {
}
// 根据指定 key,构建 value
protected V create(K key) {
return null;
}
private int safeSizeOf(K key, V value) {
int result &#61; sizeOf(key, value);
if (result <0) {
throw new IllegalStateException("Negative size: " &#43; key &#43; "&#61;" &#43; value);
}
return result;
}
// key:value 键值对大小,用于计算cache大小
// 可根据情况自定义,默认为 1
// 该 key:value 在 cache 中时大小不应该变化
protected int sizeOf(K key, V value) {
return 1;
}
public final void evictAll() {
trimToSize(-1); // -1 will evict 0-sized elements
}
// 各种方法,返回成员变量,省略
public synchronized final Map snapshot() {
return new LinkedHashMap(map);
}
&#64;Override
public synchronized final String toString() {
int accesses &#61; hitCount &#43; missCount;
int hitPercent &#61; accesses !&#61; 0 ? (100 * hitCount / accesses) : 0;
return String.format("LruCache[maxSize&#61;%d,hits&#61;%d,misses&#61;%d,hitRate&#61;%d%%]",
maxSize, hitCount, missCount, hitPercent);
}
}