Android缓存机制——LruCache
Android缓存机制——LruCache
LruCache的核心原理就是对LinkedHashMap的有效利用,它的内部存在一个LinkedHashMap成员变量,值得注意的4个方法:构造方法、get、put、trimToSize
LRU(Least Recently Used)缓存算法便应运而生,LRU是最近最少使用的算法,它的核心思想是当缓存满时,会优先淘汰那些最近最少使用的缓存对象。采用LRU算法的缓存有两种:LrhCache和DisLruCache,分别用于实现内存缓存和硬盘缓存,其核心思想都是LRU缓存算法。
LRU原理
LruCache的核心思想很好理解,就是要维护一个缓存对象列表,其中对象列表的排列方式是按照访问顺序实现的,即一直没访问的对象,将放在队尾,即将被淘汰。而最近访问的对象将放在队头,最后被淘汰。
LruCache 其实使用了 LinkedHashMap 双向链表结构,现在分析下 LinkedHashMap 使用方法。
1.构造方法:
复制代码
public LinkedHashMap(int initialCapacity,
float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder;
}
复制代码
当 accessOrder 为 true 时,这个集合的元素顺序就会是访问顺序,也就是访问了之后就会将这个元素放到集合的最后面。
例如:
复制代码
LinkedHashMap < Integer, Integer > map = new LinkedHashMap < > (0, 0.75f, true);
map.put(0, 0);
map.put(1, 1);
map.put(2, 2);
map.put(3, 3);
map.get(1);
map.get(2);
for (Map.Entry < Integer, Integer > entry: map.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
复制代码
输出结果:
0:0
3:3
1:1
2:2
下面我们在LruCache源码中具体看看,怎么应用LinkedHashMap来实现缓存的添加,获得和删除的:
复制代码
/**
* @param maxSize for caches that do not override {@link #sizeOf}, this is * the maximum number of entries in the cache. For all other caches, * this is the maximum sum of the sizes of the entries in this cache. */ 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);//accessOrder被设置为true }
复制代码
从LruCache的构造函数中可以看到正是用了LinkedHashMap的访问顺序。
2.put()方法
复制代码
/**
* 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++;//插入缓存对象加1 size += safeSizeOf(key, value);//增加已有缓存的大小 previous = map.put(key, value);//向map中加入缓存对象 if (previous != null) {//如果已有缓存对象,则缓存大小恢复到之前 size -= safeSizeOf(key, previous); } } if (previous != null) {//entryRemoved()是个空方法,可以自行实现 entryRemoved(false, key, previous, value); } trimToSize(maxSize);//调整缓存大小(关键方法) return previous; }
复制代码
可以看到put()方法重要的就是在添加过缓存对象后,调用 trimToSize()方法来保证内存不超过maxSize
3.trimToSize方法
再看一下trimToSize()方法:
复制代码
/**
* Remove the eldest entries until the total of remaining entries is at or * below the requested size. * * @param maxSize the maximum size of the cache before returning. May be -1 * to evict even 0-sized elements. */ public void trimToSize(int maxSize) { while (true) {//死循环 K key; V value; synchronized (this) {
//如果map为空并且缓存size不等于0或者缓存size小于0,抛出异常
if (size < 0 || (map.isEmpty() && size != 0)) { throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!"); }
//如果缓存大小size小于最大缓存,或者map为空,不需要再删除缓存对象,跳出循环
if (size <= maxSize) { break; }
// 取出 map 中最老的映射
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); } }
复制代码
trimToSize()方法不断地删除LinkedHashMap中队头的元素,即近期最少访问的,直到缓存大小小于最大值。
- get方法
当调用LruCache的get()方法获取集合中的缓存对象时,就代表访问了一次该元素,将会更新队列,保持整个队列是按照访问顺序排序。这个更新过程就是在LinkedHashMap中的get()方法中完成的。
接着看LruCache的get()方法
复制代码
/**
* 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. */ public final V get(K key) { if (key == null) {//key不能为空 throw new NullPointerException("key == null"); } V mapValue; synchronized (this) {
/获取对应的缓存对象
mapValue = map.get(key); if (mapValue != null) { hitCount++; return mapValue; } missCount++; }
复制代码
看到LruCache的get方法实际是调用了LinkedHashMap的get方法:
复制代码
public V get(Object key) {
LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key); if (e == null) return null; e.recordAccess(this);//实现排序的关键 return e.value; }
复制代码
再接着看LinkedHashMapEntry的recordAccess方法:
复制代码
/**
* This method is invoked by the superclass whenever the value * of a pre-existing entry is read by Map.get or modified by Map.set. * If the enclosing Map is access-ordered, it moves the entry * to the end of the list; otherwise, it does nothing. */ void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; if (lm.accessOrder) {//判断是否是访问顺序 lm.modCount++; remove();//删除此元素 addBefore(lm.header);//将此元素移到队尾 } }
复制代码
recordAccess方法的作用是如果accessOrder为true,把已存在的entry在调用get读取或者set编辑后移到队尾,否则不做任何操作。
也就是说: 这个方法的作用就是将刚访问过的元素放到集合的最后一位
5.总结:
LruCache的核心原理就是对LinkedHashMap 对象的有效利用。在构造方法中设置maxSize并将accessOrder设为true,执行get后会将访问元素放到队列尾,put操作后则会调用trimToSize维护LinkedHashMap的大小不大于maxSize。
参考:https://juejin.im/post/5b5d4e2d6fb9a04fc226c09f ; https://www.cnblogs.com/ganchuanpu/p/8908264.html
原文地址https://www.cnblogs.com/ivoo/p/10744558.html
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
企业云服务器怎么选择?【小白攻略】
目前,超过60%的大型公司正在使用或尝试将其IT相关任务迁移到云服务器或私有云架构中,云服务器能够通过消除资本支出来降低成本,并且通过将IT管理外包给服务提供商来缩减运维成本。此外,公司知道其应用和数据存放在云服务器中是安全的。 然而,选择合适的企业级云服务器可能会很具挑战性,特别是如果这意味着将您的服务从内部数据中心迁移出去。技术支持由于云服务器供应商为客户提供托管服务。 这意味着要保证IT任务的顺利运行,责任将从云服务器供应商和企业之间进行分离。云服务器提供商将负责底层硬件、服务器监控、云计算平台、操作系统和补丁等,同时企业将负责管理他们的云服务器内部环境和应用程序。 这种分离,意味着双方在技术层面上进行沟通是至关重要的。出于这个原因,选择云服务器供应商时首要的考虑因素之一高防御服务器是他们提供全年全天候专家技术支持。这种支持应包括
- 下一篇
RecyclerView封装库和综合案例【包含25篇博客】
目录介绍 1.复杂页面库介绍 2.本库优势亮点 2.1 支持多种状态切换管理 2.2 支持添加多个header和footer 2.3 支持侧滑功能和拖拽移动 2.4 其他亮点介绍 3.如何使用介绍 3.1 最基础的使用 3.2 添加下拉刷新和加载更多监听 3.3 添加header和footer操作 3.4 设置数据和刷新 3.5 设置adapter 3.6 设置条目点击事件 3.7 设置侧滑删除功能[QQ侧滑删除] 3.8 轻量级拖拽排序与滑动删除 4.关于状态切换 4.1 关于布局内容 4.2 关于实现思路 4.3 关于状态切换api调用 4.4 关于自定义状态布局 4.5 关于自定义布局交互事件处理 5.常用api介绍 5.1 状态切换方法说明 5.2 viewHolder方法说明 5.3 adapter方法说明 5.4 分割线方法说明 5.5 swipe侧滑方法说明 5.6 其他api说明 6.recyclerView的wiki文档【更新中】 6.1 封装库部分思路介绍 6.2 优化处理逻辑介绍 6.3 recyclerView相关类 6.4 recyclerView滑动冲突 6...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- MySQL8.0.19开启GTID主从同步CentOS8
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Windows10,CentOS7,CentOS8安装Nodejs环境
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- CentOS8编译安装MySQL8.0.19
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- 2048小游戏-低调大师作品
- Docker安装Oracle12C,快速搭建Oracle学习环境
- SpringBoot2整合Redis,开启缓存,提高访问速度