LRU算法简介
LRU 全称是 least recently used,意为“最近最少使用”,说白了就是一种淘汰算法,当有新的元素插入进来的时候,我们的使用空间又有限的时候,就需要淘汰旧的元素,这时候就会选择淘汰最近最少使用的元素。
应用场景
在app开发中,假设有一个页面列表需要从网络获取大量图片,在页面切换、滑动中,同一张图片不可能每次都去网络获取,这样对内存、性能和用户体验都是一个考验,最好的做法是设置本地文件缓存和内存缓存,存储从网络取得的数据。
但本地文件缓存空间并非是无限大,容量越大读取效率越低,可设置一个折中缓存容量比如10M,如果缓存已满,我们需要采用合适的替换策略换掉一个已有的数据对象,并替之已一个新的数据对象;内存缓存作为最先被读取的数据,应该存储那些经常使用的数据对象,且内存容量有限,内存缓存的容量也应该限定。
实现原理
Android中有一个LRU算法实现的集合LruCache。
public class LruCache<K, V> {
private final LinkedHashMap<K, V> map;
}
可以看出LruCache内部是使用LinkedHashMap存储数据的。其实LruCache主要依靠LinkedHashMap本身的LRU实现来实现的。LruCache只是进行了另一次封装。
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
// 这里指定了该集合的最大容量,一旦集合容量大于该容量则会调用trimToSize方法来减少容量。
this.maxSize = maxSize;
// 这里创建了LinkedHashMap并且第三个参数指定为true.该参数为true时LinkedHashMap开启LRU算法。
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
在使用LruCache时要重写sizeOf方法,来指定成员的实际容量。否则默认返回1
protected int sizeOf(K key, V value) {
return 1;
}
LruCache的一个回调函数本身没有实现,可重新。在新增或移除变量时会被调用。第一个参数为true时是移除变量,false时是新增变量。
protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}
接下来简单说下LruCache的存放方法。
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;
}
/**
*简单来说就是会无限循环的来检测内存容量
*如果内存容量大于maxSize,则会移除最近最久使用的成员。
*然后继续循环,直到内存容量小于maxSize或者集合为null循环终止
**/
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 get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
// 如果值存在则直接返回值,这里会依赖LinkedHashMap的LRU机制。
V mapValue;
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
// 这里会调用create方法,这个方法本身默认返回null,即如果用户不重写get方法会返回null。
// 根据实际需求来编写。如果创建了新的对象则会存放进集合中。
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;
}
}
资料源自互联网,经综合整理而成,如有侵犯,请联系我。