说明:
有很多开源的图片缓存库,功能强大的有内存和磁盘存储方式混用的,这里只考虑android自带的LruCache,通过它把LinkedHashMap,HashMap相关知识串起来,这也是面试中常被问到的点。
使用情景:
网络获取图片后,放到内存里,下次使用直接从内存读,不再通过网络获取,这里注意缓存容量和app可用内存的占比。
使用文档:
https://developer.android.com/reference/android/util/LruCache.html
源码位置:
android.util.LruCache.java
LruCache继承自LinkedHashMap继承自HashMap,逐个讲解。
HashMap的存储结构为数组(每个位置称之为bucket)+ 链表,K通过算法得到要存到哪个bucked里,如果哈希冲突,且key不相同则放到bucket存储的链表里。说下hash冲突的概念,hash的作用是原空间到另一空间的映射,算法肯定会出现相同的映射值,这就是冲突,对于hashmap,其冲突的意思就是K通过算法后得到的值相同(即存到相同的bucket里)。
LinkedHashMap继承HashMap,即延用HashMap的数据存储结构
对Entry多了层包装,添加双向链表的头尾指针,用来实现排序,排序规则依据accessOrder变量:false:按照插入顺序(默认);true按访问顺序
说白了就是每次访问的K会放到第一位,这样总有末尾元素,是不经常被访问的,称之为eldest。LruCache的最少使用算法就是依据这个机制实现的。
private static class LinkedHashMapEntry extends HashMapEntry {
LinkedHashMapEntry before, after;
}
private transient LinkedHashMapEntry header; //head实现双向链表
private final boolean accessOrder;
有了上面的基础,LruCache就很容易理解了,LruCache put一个元素后,put一个元素后,调用trimToSize 如果超过maxSize,删除eldest;
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);
}
}