LruCache的深入解析
来源:互联网 发布:情侣手环淘宝 互相感应 编辑:程序博客网 时间:2024/05/17 23:34
LruCache的深入解析
大家在学习图片多级缓存的时候,肯定都会接触过LruCache这个类,本片文章对LruCache进行一个独特的,深入的分析。主要分为以下两点
- LinkedHashMap介绍
- LruCache关键代码分析
分析一下LinkedHashMap这个类
* API介绍:该哈希映射的迭代顺序就是最后访问其条目的顺序,从近期访问最少到近期访问最多的顺序(访问顺序)。这种映射很适合构建 LRU 缓存。调用 put 或 get 方法将会访问相应的条目(假定调用完成后它还存在)。* 下边来通过代码来分析LinkedHashMap是否能够完成对最少访问到最多访问的一个顺序排序。* * 先看一下LinkedHashMap的构造方法在API中的介绍
LinkedHashMappublic LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)构造一个带指定初始容量、加载因子和排序模式的空 LinkedHashMap 实例。 参数:initialCapacity - 初始容量loadFactor - 加载因子accessOrder - 排序模式 - 对于访问顺序,为 true;对于插入顺序,则为 false
//定义一个map,设置排序方式为访问排序(看上边的构造方法介绍) LinkedHashMap<String, String> map = new LinkedHashMap<>(0, 0.75f, true); map.put("zhangsan", "12"); map.put("lisi", "13"); map.put("wangwu", "14"); map.put("zhaoliu", "44");
然后我来进行遍历排序打印如下
然后再简单修改一下代码
LinkedHashMap<String, String> map = new LinkedHashMap<>(0, 0.75f, true); map.put("zhangsan", "12"); map.put("lisi", "13"); map.put("wangwu", "14"); map.put("zhaoliu", "44"); map.put("wangwu", "14");
再来遍历打印如下
然后再来修改一下代码,get一下zhangsan
LinkedHashMap<String, String> map = new LinkedHashMap<>(0, 0.75f, true); map.put("zhangsan", "12"); map.put("lisi", "13"); map.put("wangwu", "14"); map.put("zhaoliu", "44"); map.put("wangwu", "14"); map.get("zhangsan");
这次遍历结果如下
经过测试,我们发现不论是添加访问还是获取访问,LinkedHashMap确实能够通过访问顺序,来进行排序。
*
* 那么下边我们来分析LruCache的源代码,这里将保留部分英文注释
*
public class LruCache<K, V> { /** * LinkedHashMap 提供特殊的构造方法来创建链接哈希映射,该哈希映射的迭代顺序就是最后访问其条目的顺序, * 从近期访问最少到近期访问最多的顺序(访问顺序)。 这种映射很适合构建 LRU 缓存。 */ private final LinkedHashMap<K, V> map; /** * 当前缓存的个数 */ private int size; /** * 缓存的最大个数 */ private int maxSize; /** * 添加到缓存中的个数 */ private int putCount; /** * 创建的个数 */ private int createCount; /** * 被移除的个数 */ private int evictionCount; /** * 命中个数 */ private int hitCount; /** * 丢失个数 */ private int missCount; /** * 最大缓存的大小,一般定缓存的大小 */ public LruCache(int maxSize) { if (maxSize <= 0) { throw new IllegalArgumentException("maxSize <= 0"); } this.maxSize = maxSize; // 定义一个LinkedHashMa this.map = new LinkedHashMap<K, V>(0, 0.75f, true); } /** * Returns the value for {@code key} if it exists in the cache or can be * created by {@code #create}. If a value was returned, it is moved to the * head of the queue. This returns null if a value is not cached and cannot * be created. 根据key,获取value值,并将本次返回的value放置到对列的最上层,来表示访问的顺序(最近访问) */ public final V get(K key) { if (key == null) { throw new NullPointerException("key == null"); } V mapValue; synchronized (this) { // 从集合中获取value mapValue = map.get(key); if (mapValue != null) { // 如果不为空,对hitCount执行自增操作 hitCount++; return mapValue; } // 如果为空,就对missCount执行自增操作 missCount++; } /* * 尝试根据key去创建这样一个value */ V createdValue = create(key); // 创建value失败,就返回一个null if (createdValue == null) { return null; } // 如果成功 synchronized (this) { // 将createCount进行自增操作 createCount++; // 并将创建的这个值存储到集合中去,并去获取该key之前所映射的值(如果之前没有映射,返回null) mapValue = map.put(key, createdValue); // 如果之前该key有对应的value值 if (mapValue != null) { // There was a conflict so undo that last put // 为了避免冲突,就重新将之前的值再存进去来覆盖我们自己创造的值(这里是考虑到了create方法是在其他线程中操作) map.put(key, mapValue); } else { // 如果之前确实没有一个value(这回终于放心了,可以使用我们自己创造的值了,就将对缓存大小做操作) // 缓存的大小改变 size += safeSizeOf(key, createdValue); } } // 这里没有移除,只是改变了位置 if (mapValue != null) { entryRemoved(false, key, createdValue, mapValue); return mapValue; } else { // 在最后判断缓存是否超过了设定的最大值 trimToSize(maxSize); return createdValue; } } /** * Caches {@code value} for {@code key}. The value is moved to the head of * the queue. * * @return the previous value mapped by {@code key}. */ 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); // 将本次添加,并获取之前的和key映射的value(也就是previous的value) previous = map.put(key, value); // 如果之前key是对应着value的,那就将之前value所占的一个缓存大小移掉(相当于一个车上某一个位子上原先有120斤重的人,现在上来一个150斤的,就应该+150 // -120) if (previous != null) { size -= safeSizeOf(key, previous); } } if (previous != null) { entryRemoved(false, key, previous, value); } // 看一下当前大小是否超过总的大小 trimToSize(maxSize); return previous; } /** * Remove the eldest entries until the total of remaining entries is at or * below the requested size. 如果我们定义的size>maxSize 就移除一个最不常用的数据(或者是 * 最老的数据),直到我们定义的size<maxSize * * @param maxSize * the maximum size of the cache before returning. May be -1 to * evict even 0-sized elements. */ public void trimToSize(int maxSize) { /** * 这里是一个无限循环的操作,如果我们的size一直大于maxSize,就一直执行这个方法 比如车的载重是400斤,之前有四个人,分别是 * 100斤,99斤,98斤,97斤,现在上来一个人是300斤,那就得执行移除三个人的方法,才能满足不超重 */ 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 || map.isEmpty()) { break; } // 移除最少使用的缓存// Set<Entry<K,V>> entrySet = map.entrySet(); Map.Entry<K, V> toEvict = map.entrySet().iterator().next(); key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); // 将当前的size减掉所移除的size size -= safeSizeOf(key, value); evictionCount++; } entryRemoved(true, key, value, null); } } /** * Removes the entry for {@code key} if it exists. * 用户手动移除 * * @return the previous value mapped by {@code key}. */ public final V remove(K key) { if (key == null) { throw new NullPointerException("key == null"); } V previous; synchronized (this) { previous = map.remove(key); if (previous != null) { size -= safeSizeOf(key, previous); } } if (previous != null) { entryRemoved(false, key, previous, null); } return previous; } /** * //这里用户可以重写它,实现数据和内存回收操作,默认是没有任何实现 */ protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) { } /** * Returns the size of the entry for {@code key} and {@code value} in * user-defined units. The default implementation returns 1 so that size is * the number of entries and max size is the maximum number of entries. * //这里跟我们实例化 LruCache 的 maxSize 要对应起来,如果 maxSize在初始定义的时候是定义的个数 这里就是 return * 1,如果是内存的大小,如果5M,这是应该是每个缓存 value 的 size 大小,如果是 Bitmap,这应该是 * bitmap.getByteCount(); 所以这个方法一般会由用户进行重写 * */ protected int sizeOf(K key, V value) { return 1; } /** * Clear the cache * 清除所有缓存 */ public final void evictAll() { trimToSize(-1); // -1 will evict 0-sized elements } /** * Returns a copy of the current contents of the cache, ordered from least * recently accessed to most recently accessed. 返回缓存的一个备份,从最不常用到最常用进行排序 */ public synchronized final Map<K, V> snapshot() { return new LinkedHashMap<K, V>(map); }
以上代码是LruCache的关键代码,已在代码中给出注释,大家在学习的时候,可以自己导入v4包的源码来研究这个类的源码。
本篇文章是对LruCache的一个源码解析,如想对图片多级缓存(包括DiskLruCache及缓存原理)了解更多,请将关注本人。
14 1
- LruCache的深入解析
- LruCache的终极解析
- 关于LRUCache的实现和源码解析
- 深入源码剖析LruCache
- LruCache源码解析
- LRUCache源码解析
- android LRUCache解析
- LruCache源码解析
- LruCache源码完全解析
- 源码全面解析---LruCache
- LruCache源码解析
- LruCache 源码解析
- LruCache源码解析
- android LruCache源码解析
- LruCache源码解析
- LruCache源码解析
- LruCache源码完全解析
- LruCache 源码解析
- vb6.0通过控件来连接远程sql数据库
- AndroidStudio下加入百度地图的使用(一)——环境搭建
- NY55 懒省事的小明
- 走近AbstractQueuedSynchronizer
- Go语言黑魔法中的问题修正
- LruCache的深入解析
- ZOJ 3326 日历 (打表)
- 数据库四种隔离级别
- discuz的php7版本
- python入门笔记_1
- 基于libRTMP的流媒体直播之 AAC、H264 推送
- IPTABLES设置
- HDU Digital Roots
- 前端浏览器兼容问题部分整理