LRUCache原理
声明的变量:
//存放数据的集合
private final LinkedHashMap map;
//当前LruCahce的内存占用大小
private int size;
//Lrucache的最大容量
private int maxSize;
//put的次数
private int putCount;
//create的次数
private int createCount;
//回收的次数
private int evictionCount;
//命中的次数
private int hitCount;
//丢失的次数
private int missCount;
构造函数:
public LruCache(int maxSize) {
if(maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
} else {
this.maxSize = maxSize;
this.map = new LinkedHashMap(0, 0.75F, true);
}
}
这里设置了maxSize,以及实例化了一个LinkedHashMap对象,这个LinkedHashMap对象是实现Lru算法的关键,Lru是最近最少使用算法的简称,意思呢就是查询出最近的时间使用次数最少的那个对象。
new LinkedHashMap
运行以下代码:
public static final void main(String[] args) {
LinkedHashMap map = new LinkedHashMap<>(0, 0.75f, true);
map.put(0, 0);
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);
map.put(4, 4);
map.put(5, 5);
map.put(6, 6);
map.get(1);
map.get(2);
for (Map.Entry entry : map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
}
输出为:
0:0
3:3
4:4
5:5
6:6
1:1
2:2
将上述代码LinkedHashMap初始化最后一个参数改为false输出为:
0:0
1:1
2:2
3:3
4:4
5:5
6:6
有以上结果可以看出,这个设置为true的时候,如果对一个元素进行了操作(put、get),就会把那个元素放到集合的最后,设置为false的时候,无论怎么操作,集合元素的顺序都是按照插入的顺序来进行存储的。
到了这里我们可以知道,这个LinkedHashmap正是实现Lru算法的核心之处,当内容容量达到最大值的时候,只需要移除这个集合的前面的元素直到集合的容量足够存储数据的时候就可以了。
put方法:
public final V put(K key, V value) {
if(key != null && value != null) {
Object previous;
synchronized(this) {
++this.putCount;
this.size += this.safeSizeOf(key, value);
//此处map.put返回的结果为put之前当前key所取到的值,具体可以参考hashmap的实现
previous = this.map.put(key, value);
if(previous != null) {
this.size -= this.safeSizeOf(key, previous);
}
}
if(previous != null) {
this.entryRemoved(false, key, previous, value);
}
this.trimToSize(this.maxSize);
return previous;
} else {
throw new NullPointerException("key == null || value == null");
}
}
由上面的代码可以看出来,首先把size增加,然后判断是否以前已经有元素,如果有,就更新当前的size,并且调用entryRemoved方法,entryRemoved是一个空实现,如果我们使用LruCache的时候需要掌握元素移除的信息,可以重写这个方法。最后就会调用trimToSize,来调整集合中的内容。
trimToSize方法:
public void trimToSize(int maxSize) {
while(true) {
Object key;
Object value;
synchronized(this) {
if(this.size < 0 || this.map.isEmpty() && this.size != 0) {
throw new IllegalStateException(this.getClass().getName() + ".sizeOf() is reporting inconsistent results!");
}
if(this.size <= maxSize || this.map.isEmpty()) {
return;
}
Entry toEvict = (Entry)this.map.entrySet().iterator().next();
key = toEvict.getKey();
value = toEvict.getValue();
this.map.remove(key);
this.size -= this.safeSizeOf(key, value);
++this.evictionCount;
}
this.entryRemoved(true, key, value, (Object)null);
}
}
当当前size大于maxsize时,循环移除map中的第一个元素,直到达到跳出循环的条件。由上面的分析知道,map中的第一个元素就是最近最少使用的那个元素。
get方法:
public final V get(K key) {
if(key == null) {
throw new NullPointerException("key == null");
} else {
Object mapValue;
synchronized(this) {
mapValue = this.map.get(key);
if(mapValue != null) {
++this.hitCount;
return mapValue;
}
++this.missCount;
}
Object createdValue = this.create(key);
if(createdValue == null) {
return null;
} else {
synchronized(this) {
++this.createCount;
mapValue = this.map.put(key, createdValue);
if(mapValue != null) {
this.map.put(key, mapValue);
} else {
this.size += this.safeSizeOf(key, createdValue);
}
}
if(mapValue != null) {
this.entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
this.trimToSize(this.maxSize);
return createdValue;
}
}
}
}
这个方法就先通过key来获取value,如果能获取到就就直接返回,获取不到的话,就调用create()方法创建一个。创建完成后key是否能取出值,如果仍取不出则将创建的对象放入map并返回,否则回退返回根据key取出的旧的值。
其它方法没什么好分析的。以上