位置: 编程技术 - 正文

深入源码剖析LruCache(如何分析源码)

编辑:rootadmin

推荐整理分享深入源码剖析LruCache(如何分析源码),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:quagga源码分析,源码分析怎么写,xen源码分析,源码分析怎么写,源码分析是什么意思,源码分析怎么写,源码分析,源码分析怎么写,内容如对您有帮助,希望把文章链接给更多的朋友!

引言:最近许多人在博客中提到自己在面试时被问“LruCache 的原理是?”,发现自己之前完全没有接触过这个知识点,本着知其然知其所以然的态度,先搜索了一些博文了解相关知识,就去看源码了。现在大概知道 LruCache 是啥,写个博文权当是学习笔记把

LruCache 的前世今生LruCache 是何方神圣?

我一般不喜欢野路子的定义,所以我摘选了 Android 官方对 LruCache 的定义:

A cache that holds strong references to a limited number of values. Each time a value is accessed, it is moved to the head of a queue. When a value is added to a full cache, the value at the end of that queue is evicted and may become eligible for garbage collection.

定义的意思是:LruCache 是对限定数量的缓存对象持有强引用的缓存,每一次缓存对象被访问,都会被移动到队列的头部。当有对象要被添加到已经达到数量上限的 LruCache 中,队列尾部的对象将会被移除,而且可能会被垃圾回收机制回收。

所以从定义里我们可以知道:LruCache 中有一个队列存储对象的访问顺序,在 LruCache 达到存储上线时,将会通过移除队列中的队尾元素为需要添加进来的元素腾出空间。

LruCache 中的队列的存在意义是什么?

要探讨这个问题首先我们要知道的是:LruCache 中的 Lru 指的是“Least Recently Used-近期最少使用算法”。这就意味着,LruCache 应该是一个能够判断哪个缓存对象是近期最少使用的缓存类。解释到这里我相信大家都能明白为什么需要引入队列了。

引入队列的意义在于,每一次 LruCache 中的某个缓存对象被访问,该对象在队列中的位置就会发生改变——即提到队列的头部,假设队列有 n 个元素,n-1 个元素都被不同频率地访问过,唯独元素 A 一直没有被访问,那么 A 必然处于队尾,成为“近期最少使用元素”了。

LruCache 的实现原理LruCache 初步源码分析

代码太多,我不可能全都贴上来,所以我就按照我的思路来贴啦~

从代码里我们可以知道:LruCache 不是其他类的子类,并且其中实际涉及到的只有一个 LinkedHashMap 对象和一堆 int 值。也就是说,LruCache 实现其核心逻辑的关键应该就是 LinkedHashMap。

那我们就先来看看 LinkedHashMap的源码吧。

LinkedHashMap 剖析LinkedHashMap 是什么

同样的,引用官方的解释:

LinkedHashMap is an implementation of Map that guarantees iteration order. All optional operations are supported.

Entries are kept in a doubly-linked list. The iteration order is, by default, the order in which keys were inserted. Reinserting an already-present key doesn’t change the order. If the three argument constructor is used, and accessOrder is specified as true, the iteration will be in the order that entries were accessed. The access order is affected by put, get, and putAll operations, but not by operations on the collection views.

LinkedHashMap 是 Map 的一种实现(HashMap 的子类,HashMap 的父类是抽象 Map 类,其中实现了 Map 接口),能够保证迭代器的顺序。LinkedHashMap 中的数据通过一个双向链表存储,默认情况下,迭代器的顺序为键的掺入顺序,并且重新插入一个已经存在的键不会改变迭代器的顺序。

如果构造方法中的第三个参数 boolean accessOrder 被使用,而且 accessOrder 的值为 true,那么迭代器的顺序就是数据的访问顺序,并且,这个顺序将受 put,get,putall 这些方法影响,但不受集合视图操作的影响。

LinkedHashmap 的解释证明了我们刚刚的猜测 LruCache 实现其核心逻辑的关键应该就是 LinkedHashMap 是对的,那么我们只要弄懂了 LinkedHashpMap 的具体原理,LruCache 的原理也不是问题了。

LinkedHashMap 使用范例

在分析 LinkedHashMap 之前,我们不妨先看看 LinkedHashMap 到底能干些什么,毕竟从他的外在表现去分析内在逻辑会更轻松些,下面是一段简单的 Java 代码:

深入源码剖析LruCache(如何分析源码)

这是输出:

处理前 one two three 处理后 two three one

我们可以看到,处理后 one 的位置显然发生了改变,跑到了 two,three 的前面。

LinkedHashMap 实现原理

我们直接看 LinkedHashMap 中相关的第三个构造方法吧:

我们可以看到,构造方法调用了父类的构造方法,创建了一个 LinkedEntry 对象,并把 accessOrder 的值设为构造方法传入的值。那这个 LinkedEntry 是什么呢?

我们可以看到,LinkedEntry 就是用于存储数据的双向链表。所以,我们现在只需要知道 LinkedHashMap 类是如何根据 accessOrder 的值,决定程序使用 get,put 等方法将如何操作 LinkedEntry 就可以知道一整个 LinkedHashMap 的实现原理了。

我们追踪 accessOrder 的值很容易发现在 get 方法中有相应的处理:

从代码中可以看到,只要 accessOrder 为 true,就会执行 makeTail 方法操作一个新建的 LinkedEntry 对象,我们不妨进入 makeTail 方法一探究竟:

相信这里的逻辑稍微学过数据结构的朋友都能看懂了,就是改变某个结点的位置,把它放到头部。

get 带来的队列变化我们是知道了,那 put 呢?我们一搜索 put,发现类里没有这个方法,这是什么鬼……莫慌,我们耐心地翻翻代码会发现 addNewEntry(K key, V value, int hash, int index) 和 addNewEntryForNullKey(V value) 方法,而它们就是实现 put 改变队列逻辑的两个方法了,我们不妨看看:

addNewEntry 方法中,当有新元素加入时就会执行进行判断,决定是否移除过期的元素,当我们进入 removeEldestEntry 方法会发现,removeEldestEntry 方法的返回值恒为 false,也就是说过期元素无论如何都不会被移除,那这是什么意思呢?

其实这挺好理解的,LinkedHashMap 作为一个存储结构,它并没有限制自身的存储容量(因为他的实现基于一个双向链表),所以他没有理由在默认实现中移除过期元素,而 LruCache 中则是自己设计了相应的逻辑去处理过期元素。

再探 LruCache

通过刚刚的分析,我们已经知道 LruCache 的核心 LinkedHashMap 是如何提取“近期最少使用对象”的了,那么 LruCache 中还利用 LinkedHashMap 的特性做了什么事呢?大家跟着我继续探索吧~

通过 get/put 方法我们可以知道,LruCache 是通过 trimToSize 来控制其中的元素数量的,当达到数量上限,添加元素则会将过期元素移除:

代码首先作一些简单判断,只有 size > maxSize 才会执行相关逻辑:遍历 LinkedHashMap 双向链表的元素,取得队列尾部的元素(就是我们所说的过期元素),如果它不为空,就将它移除。

注意事项

1、如果你的缓存对象持有需要被精确回收的资源,重写 entryRemoved 方法能完成你的需求。

2、默认情况下,缓存空间的大小由元素的数量决定,但你可以在不同的单元中重写 sizeOf 方法改变大小。例如:bitmap 对象大小为 4m

3、传入 LruCache 的键值对不能是 null,因为 get/put/remove 方法的含义会因此变得模糊。

adb push、adb install 和强制安装 1.adbpushXXX.apk目录是将apk发送到手机指定的目录adbpushtest.apk/sdcard/test/test.apk2.adbinstall电脑中apk的路径是安装电脑中的apk到手机adbinstall/Users/test/test.apk3.强

使用Ant自动签名、打包Android apk并且自动安装到手机 一、建立Ant打包Apk新建一个TestAnt项目创建App的签名密钥参考我的这篇github,欢迎Star|点击这里取到密钥后,在项目中创建一个keystore的文件夹,复制密

App开发日报 -- Android 5.X系列之如何阅读源码 App开发日报--@好东西传送门出品,过刊见

标签: 如何分析源码

本文链接地址:https://www.jiuchutong.com/biancheng/382258.html 转载请保留说明!

上一篇:Android快速开发--使用ORMLite操作数据库(android 快速开发工具)

下一篇:adb push、adb install 和强制安装

  • 筹建期间取得的利息收入 企业所得税
  • 用友软件生成凭证合并进项税
  • 累计折旧贷方余额表示
  • 其他应收款减值测试注意什么
  • 自然人税收管理系统扣缴客户端
  • 企业不开票的收入会怎么样处理
  • 银行手续费发票图片
  • 公司代个人收承兑汇票
  • 营改增行业的销售额
  • 公司对外投资是股东会还是董事会
  • 试分析营改增的重大意义
  • 海关特许权使用费 公告
  • 股权增资稀释股价会涨吗
  • 开发人员选项怎么改定位
  • 新办企业的开办费用应计入( )
  • 工资薪金所得的个人所得税筹划方法
  • 公司房产税如何计算器
  • 汽车维修公司如何经营粉丝群才能让潜在客户注意到我们
  • 什么样的发票需要交税
  • 融资租赁直租会查征信吗
  • 使窗口最小化的快捷键
  • mac怎么安装dmg软件
  • 货物运输业的增值税税率
  • 怎么写会计凭证
  • 免费样品销售给客户怎么入账
  • 怎么通过mac地址访问设备
  • php判断为空的方法有哪些
  • 外包社保会计分录
  • rtvscn95.exe - rtvscn95是什么进程 有什么用
  • 桌面级cpu天梯图快科技
  • lsass.exe是什么程序
  • 不能进行加计扣除的研发费用有哪些
  • cpu和gpu性能对比
  • 计算所得税费用公式excel
  • 恩智浦杯官网
  • 以摊余成本计量和以公允价值计量的区别
  • jmeter接口串联
  • 关于低值易耗品的说法中不正确的是
  • 农机销售融资贷款流程
  • 管理费用主要核算内容包括什么?
  • 发票金额少于付款金额怎么做账
  • 预收账款是什么要素
  • 房地产企业 预缴
  • 固定资产超过多少入账
  • 企业购买商场的资本金要求是多少
  • 在防控新型冠状病毒肺炎期间经营者违反价格法
  • 其他应收款在借方怎么调账
  • 未分配利润科目余额在借方还是贷方
  • 生产成本里面的直接人工
  • 第二年发票可以入上年账吗
  • 给职工交的商业险是什么
  • 利息收入和利息费用是一个科目吗
  • 新收入准则要求
  • 注册资本越多越好吗
  • 会计电算化建账的基本流程有哪些
  • mysql数据库技术介绍
  • xp怎么删除多余的操作系统
  • mac苹果系统怎么用
  • windowsxp怎么装windows7
  • linux clk
  • Linux磁盘配额步骤
  • 3dmax创建图形怎么用
  • javascript概述及作用
  • linux如何创建守护进程
  • python获取数据包
  • 在javascript中逻辑运算符包括
  • shell脚本自动化
  • js动态生成的id怎样获取
  • python 源码解析
  • jquery中判断某个类是否存在的方法
  • javascript parseInt 函数分析(转)
  • javascript 性能
  • jQuery使用Selectator插件实现多选下拉列表过滤框(附源码下载)
  • android完整开源项目
  • js自适应布局
  • 减免性质代码怎么会自动选择
  • 小规模企业所得税申报流程
  • 内蒙古国地税联合网厅
  • 精神残疾人员是残疾人吗
  • 辽宁网上税务
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设