LRU(Least Recently Used)最近最少使用,是Android提供的一个缓存类,内部则由LinkedHashMap
实现
先来看LruCache
中的属性:
size
当前所缓存的对象数量
maxSize
当前LruCache
所能承受的容量
putCount
添加对象的次数
createCount
添加的次数
evictionCount
hitCount
命中次数,根据key能够正确查到指定的value
的次数
missCount
未命中次数,根据key未能查到指定的value
的次数
map
是一个LinkedHashMap
存放对象,是LruCache的核心
构造器
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap(0, 0.75f, true);
}
传入一个最大容量,并初始化map
;
put
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;
}
可以看到这里面加了一个同步锁synchronized
,我们来看看里面,首先putCount++
,然后通过safeSizeOf
计算传入对象的大小,并加入到size
,然后再将传入的对象添加到集合map
中,如果传入的key值在集合中有对应的元素,还需要对原始元素的大小进行计算,最后减去原始元素的值,同步块之外还有两个方法entryRemoved() trimToSize()
,加上前面说的计算对象大小的方法safeSizeOf()
我们一起来看下
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;
}
可以看到safeSizeOf()
中具体计算元素大小的地方是sizeOf()
,而此方法默认返回为1,但是我们可以重写此方法,通过我们具体的业务来进行具体的元素类型大小的计算,
LinkedHashMap#entryRemoved():
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
默认是个空方法,但是在网上找了些介绍文字:
put发生key冲突时被调用,evicted = false,key为传入的key值,oldValue为该key所对应的原始value值,newValue为传入的value值
trimToSize的时候会被调用一次,就是最后一次被删除的最少访问数据带回来,evicted = true,newVlue = null
remove的时候,存在对应的key,并且被成功删除后调用,evicted = false, newVlue = null
再者这个方法的应用场景,我没有找到,虽然返回了原始值,但是原始值也可能被提前回收了,
LruCache#trimToSize():
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 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);
}
}
整体是一个死循环,结束循环的条件是
当前集合中的元素总大小 < 设置集合的总容量
map.eldest() 返回当前集合的头结点,如果头结点为空,即集合为空,直接返回循环
综上可以看出此方法的作用是:当集合的元素的总大小大于指定的元素大小使,用来删除一些元素来空出一部分空间的操作。而删除的节点是从头结点开始的,符合访问顺序的特性,删除最近没有被访问的节点
get
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++;
}
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值,hitCount++
,并返回找到的key值,如果没有找到则missCount++
,并且通过create()
创建一个value
值,如果这个值不为空则createValue++
并将此key
以及value
放入到集合中。
create()
是一个空方法,我们可以自己去继承实现
LruCache lruCache = new LruCache(50){
//自己实现的计算元素大小的方法
@Override
protected int sizeOf(String key, Bitmap value) {
return super.sizeOf(key, value);
}
// 重新定义集合的最大容量
@Override
public void resize(int maxSize) {
super.resize(maxSize);
}
// 如果没有找到此key对应的value,通过此方法可以创建一个value值
@Override
protected Bitmap create(String key) {
return super.create(key);
}
};