1.核心思想 (Least Recently Used Cache)
当缓存空间不足时,优先淘汰那些最近最少使用的数据
(Least 是little的最高级)
2.原理
LruCache<Object,Object> lruCache=new LruCache<>(10);
lruCache.put("key","value");
lruCache.get("key");
LruCache内部维持了一个LinkedHashMap,参数accessOrder=true,表示会按照元素的访问顺序进行排序, 当我们往链表尾部中插入一个元素时,会检查链表的容量超过设定值,如果超过,就把链表头部(最近最少)的元素删除,当我们访问链表的一个元素时,我们会把这个元素插入到链表的尾部.
2.源码解析
-
双向链表
-
当accessOrder为false时(默认值),LinkedHashMap按照元素的插入顺序进行排序。此时,双向链表的作用主要是记录元素的插入顺序,使得LinkedHashMap能够像List那样按照元素插入的顺序进行迭代。
-
当accessOrder为true时,LinkedHashMap则会按照元素的访问顺序进行排序。这意味着,每次访问(通过get或put方法)一个元素时,该元素都会被移动到双向链表的末尾。这样,最近使用的元素就会出现在链表的末尾,而最近最少使用的元素则会出现在链表的开头。
-
LruCache使用 ,put方法调用的是HashMap的put方法,newNode和newTreeNode方法是LinkedHashMap重写实现的,使用双向链表保存Node节点,get方法也是调用LinkedHashMap重写的get方法
-
使用方式
-
源码实现
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
//内部是基于LinkedHashMap实现的
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);//accessOrder=true
}
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);
//LinkedHashMap插入节点,如果插入节点存在,返回旧值,否则返回null
previous = map.put(key, value); //调用HashMap的put方法
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
trimToSize(maxSize); //检测是否超过链表容量,如果超过,删除头部节点
return previous;
}
public final V get(K key) {
mapValue = map.get(key);//调用LinkedHashMap的get方法
trimToSize(maxSize);//检测是否应该删除头结点
}
public void trimToSize(int maxSize) {
while (true) {
Map.Entry<K, V> toEvict = map.eldest();//获取双向链表的头节点
if (size <= maxSize) {
break;
}
//如果size超过链表最大容量
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key); //删除头结点
size=size-1;
}
}
}
//获取双向链表头节点
public Map.Entry<K, V> eldest() {
return head;
}
//LinkedHashMap获取元素
public V get(Object key) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) == null)
return null;
if (accessOrder)
afterNodeAccess(e); //把e移动到双向链表的尾部节点
return e.value;
}
void afterNodeAccess(Node<K,V> e) { // move node to last
LinkedHashMapEntry<K,V> last;
if (accessOrder && (last = tail) != e) {
LinkedHashMapEntry<K,V> p =
(LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
p.after = null;
if (b == null)
head = a;
else
b.after = a;
if (a != null)
a.before = b;
else
last = b;
if (last == null)
head = p;
else {
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}