LruCache的深入解析

大家在学习图片多级缓存的时候,肯定都会接触过LruCache这个类,本片文章对LruCache进行一个独特的,深入的分析。主要分为以下两点

  • LinkedHashMap介绍
  • LruCache关键代码分析

分析一下LinkedHashMap这个类

* API介绍:该哈希映射的迭代顺序就是最后访问其条目的顺序,从近期访问最少到近期访问最多的顺序(访问顺序)。这种映射很适合构建 LRU 缓存。调用 put 或 get 方法将会访问相应的条目(假定调用完成后它还存在)。
* 下边来通过代码来分析LinkedHashMap是否能够完成对最少访问到最多访问的一个顺序排序。
*
* 先看一下LinkedHashMap的构造方法在API中的介绍

LinkedHashMap
public 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;// 定义一个LinkedHashMathis.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) {// 从集合中获取valuemapValue = map.get(key);if (mapValue != null) {// 如果不为空,对hitCount执行自增操作hitCount++;return mapValue;}// 如果为空,就对missCount执行自增操作missCount++;}/** 尝试根据key去创建这样一个value*/V createdValue = create(key);// 创建value失败,就返回一个nullif (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减掉所移除的sizesize -= 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及缓存原理)了解更多,请将关注本人。

LruCache的深入解析相关推荐

  1. LruCache算法原理解析

    本来想写关于LruCache算法的原理解析文章,但是看到卡神的作品,写的太好了.所以这里我还是转载一下吧. 史上最详细LrcCache算法解析:https://blog.csdn.net/carson ...

  2. LruCache源码解析

    lrucache,最近最少使用缓存策略,源码其实很简单,没有多少行.下面我们分两个部分来解析: 第一部分:如何使用 /*** 存储的key类型* 存储的value类型* 设置最大存储容量* 计算每个存 ...

  3. LruCache 源码解析

    前言: 没看懂LruCache之前,我擦, LruCache这个类设计有啥意义? LinkedHashMap 完全可以代替.看懂之后,设计的真好. LruCache 概念描述 一般来说,缓存策略主要包 ...

  4. LruCache的终极解析

    LruCache是android提供的一个常用而且有用的数据缓存工具类,通其中是通过键值对来保存数据.对于LruCache首先看看原文的说明: A cache that holds strong re ...

  5. android sdk 源码解析

    AndroidSdkSourceAnalysis:https://github.com/LittleFriendsGroup/AndroidSdkSourceAnalysis 第一期 Class 分析 ...

  6. Glide 4.9源码解析-缓存策略

    本文Glide源码基于4.9,版本下载地址如下:Glide 4.9 前言 在分析了Glide的图片加载流程后,更加发觉到Glide的强大,于是这篇文章将继续深入分析Glide的缓存策略.不过今天的文章 ...

  7. LruCache之LruCache分析

    转LruCache之LruCache分析 LruCache 分析 LruCache 是 Android 的一个内部类,提供了基于内存实现的缓存 用法 //获取系统分配给每个应用程序的最大内存,每个应用 ...

  8. Android中图片的三级缓存策略

    在开发过程中,经常会碰到进行请求大量的网络图片的样例.假设处理的不好.非常easy造成oom.对于避免oom的方法,无非就是进行图片的压缩.及时的回收不用的图片.这些看似简单可是处理起来事实上涉及的知 ...

  9. 快速从入门到精通,建议细读

    前言 我们程序员之所以担心35岁被裁员,是因为自己的经验和实力能力不符合,国内IT现状,大多数程序员都在外包公司工作,框架老化,代码就是为了完成任务而写,刚定好需求你做一半产品突然让你改需求--一切的 ...

最新文章

  1. 我现在是个普通Java程序员,如何才能“更有竞争力”?
  2. Android-使用FindBugs
  3. 【Thymeleaf】 循环固定次数/循环次数由变量控制
  4. ios 导航栏(自己定义和使用系统方式)
  5. BigDFT的编译运行
  6. 【一起学爬虫】PyQuery详解
  7. 【幅度调制】基于matlab GUI语音幅度调制【含Matlab源码 292期】
  8. GCC编译器使用简介
  9. 思科命令敲错等待解决方案
  10. matlab 并联机械臂_MATLAB机械臂的两种路径规划
  11. 尘梦留痕:苏东坡的诗词里,藏着六种人生智慧
  12. 4款国产实用软件,只因功能强大,常被误认为是外国人开发的
  13. 基于Layui自定义内容轮播插件
  14. Errors while compiling. Reload prevented
  15. 2019年人工智能研发热点回眸
  16. 13. 软件包详解,rpm包的查找,安装,升级,卸载,验证等所有操作
  17. MySQL的定时任务EVENT事件使用说明
  18. 关于SQL注入靶场搭建及过关教程
  19. mt6735 ALSA Driver内部功放如何切到CLASSD
  20. Unity和VS2017下载

热门文章

  1. Stata:时变Granger因果检验
  2. # Classification: Accuracy(准确率)
  3. 【tensorflow】conv2d/conv3d/maxpool2d/maxpool3d
  4. python文件(file)路径(Path, path Windows)
  5. Python的 yield用法
  6. 设计,让交叉口更安全
  7. 二叉树的遍历(非递归)整理
  8. 【HTML5】HTML语法和基本常用标签(字符集)
  9. “李焕英效应”将长期主宰中国影视股投资逻辑
  10. PHP基础——安装Apache软件