LruCache 源码分析
Posted yinhuanxu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了LruCache 源码分析相关的知识,希望对你有一定的参考价值。
我在参加笔试的时候,有一道题是设计一个 LruCache,当时由于不理解原理而没有写出来,现在看了几遍源码,记录下笔记理清思路。
LruCache 的底层实现是 LinkedHashMap 。
先看到 LruCache 的构造方法:
public LruCache(int maxSize)
if (maxSize <= 0)
throw new IllegalArgumentException("maxSize <= 0");
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
配置了缓存的最大值,然后创建了一个 LinkedHashMap 对象,如果你了解 LinkedHashMap,会知道这个 Map 是可以实现 LRU 算法的。
而这里恰好开启了 LRU 算法,没错,LinkedHashMap 构造方法第三个参数传进 true 就是了。
我们来看 LruCache 的 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;
可以看到,key 和 value 都不允许为 null。在 synchronized 代码块中,调用了 LinkedHashMap 的 put 方法将 key 和 value 传进,缓存起来。而且每一次调用 put 方法,都会调用 trimToSize 方法。
你应该和我一样关心 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<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);
看到这一行核心代码
Map.Entry<K, V> toEvict = map.eldest()
ps:Evict 是逐出的意思
可以看到这里调用了 LinkedHashMap 的 eldest 方法,
public Map.Entry<K, V> eldest()
Entry<K, V> eldest = header.after;
return eldest != header ? eldest : null;
这个方法取出了 LinkedHashMap 的双向链表头节点的下一个节点。
补充一下LinkedHashMap:
LinkedHashMap 在开启 LRU 算法后,每次调用 put 方法和 get 方法,都会将(最近最常访问的)元素放在链接的结尾。也就是说,多次调用 LinkedHashMap 的 put 和 get 方法,位于链表前面的元素,就是最近最少使用的了,应该被移除。
而这里取出头节点的下一个节点,肯定是移除它的啦。这也是合理的。好,我们继续看到 LruCache 的 trimToSize 方法,之后就是调用了 HashMap 的 remove 方法来移除元素了。(LinkedHashMap 是继承自 HashMap 的)
全文完。
以上是关于LruCache 源码分析的主要内容,如果未能解决你的问题,请参考以下文章