首页 > 代码库 > 【源代码】LruCache源代码剖析
【源代码】LruCache源代码剖析
上一篇分析了LinkedHashMap源代码,这个Map集合除了拥有HashMap的大部分特性之外。还拥有链表的特点,即能够保持遍历顺序与插入顺序一致。
另外。当我们将accessOrder设置为true时。能够使遍历顺序和訪问顺序一致,其
内部双向链表将会依照最近最少訪问到最近最多訪问的顺序排列Entry对象,这能够用来做缓存。这篇文章分析的LruCache并非jdk中的类,而是来自安卓,熟悉安卓内存缓存的必定对这个类不陌生。
LruCache内部维护的就是一个LinkedHashMap。
以下開始分析LruCache。
注:以下LruCache源代码来自support.v4包。
首先是这个类的成员变量:
private final LinkedHashMap<K, V> map; /** Size of this cache in units. Not necessarily the number of elements. */ private int size;//当前大小 private int maxSize;//最大容量 private int putCount;//put次数 private int createCount;//create次数 private int evictionCount;//回收次数 private int hitCount;//命中次数 private int missCount;//丢失次数LinkedHashMap的初始化放在构造器中:
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的accessOrder设置为true。
接下来看两个最重要的方法,put和get。
首先是put方法:
public final V put(K key, V value) { if (key == null || value =http://www.mamicode.com/= null) {//键值不同意为空>put方法无非就是调用LinkedHashMap的put方法。可是这里在调用LinkedHashMap的put方法之前,推断了key和value是否为空,也就是说LruCache不同意空键值。除此之外,put操作被加锁了,所以是线程安全的!
既然是缓存,那么必定可以动态删除一些不经常使用的键值对,这个工作是由trimToSize方法完毕的:public void trimToSize(int maxSize) { while (true) {//不断删除linkedHashMap头部entry,也就是近期最少訪问的条目。直到size小于最大容量 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 || map.isEmpty()) {//直到容量小于最大容量为止 break; } Map.Entry<K, V> toEvict = map.entrySet().iterator().next();//指向链表头 key = toEvict.getKey(); value = http://www.mamicode.com/toEvict.getValue();>这种方法不断循环删除链表首部元素。也就是近期最少訪问的元素,直到容量不超过预先定义的最大值为止。注:LruCache在android.util包中也有一个LruCache类,可是我发现这个类的trimToSize方法是错误的:private 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 = null; for (Map.Entry<K, V> entry : map.entrySet()) { toEvict = entry; } if (toEvict == null) { break; } key = toEvict.getKey(); value = http://www.mamicode.com/toEvict.getValue();>这里的代码将会循环删除链表尾部,也就是近期訪问最多的元素,这是不对的!所以大家在做内存缓存的时候一定要注意,看trimToSize方法是否有问题。接下来是get方法:public final V get(K key) { if (key == null) {//不同意空键 throw new NullPointerException("key == null"); } V mapValue; synchronized (this) {//线程安全 mapValue = http://www.mamicode.com/map.get(key);//调用LinkedHashMap的get方法>get方法即依据key在LinkedHashMap中寻找相应的value,此方法也是线程安全的。以上就是LruCache最重要的部分,以下再看下其它方法:remove:public final V remove(K key) { if (key == null) { throw new NullPointerException("key == null"); } V previous; synchronized (this) { previous = map.remove(key);//调用LinkedHashMap的remove方法 if (previous != null) { size -= safeSizeOf(key, previous); } } if (previous != null) { entryRemoved(false, key, previous, null); } return previous;//返回value }
sizeof:这种方法用于计算每一个条目的大小,子类必须得复写这个类。protected int sizeOf(K key, V value) {//用于计算每一个条目的大小 return 1; }snapshot方法,返回当前缓存中全部的条目集合public synchronized final Map<K, V> snapshot() { return new LinkedHashMap<K, V>(map); }总结:1.LruCache封装了LinkedHashMap。提供了LRU缓存的功能;2.LruCache通过trimToSize方法自己主动删除近期最少訪问的键值对。3.LruCache不同意空键值;4.LruCache线程安全。5.继承LruCache时,必需要复写sizeof方法。用于计算每一个条目的大小。
【源代码】LruCache源代码剖析
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。