LRUCache

LRUCache是Android中实现内存缓存相关的组件类,当缓存满时其使用最近最少使用策略来淘汰相关的元素,以控制缓存大小。本文主要基于LRUCache相关源码分析LRUCache的创建、缓存的添加、获取、删除流程。

LRUCache创建

LRUCache的创建可以直接看其构造函数

	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来存储缓存对象的引用。
当我们使用LRUCache时一般如下:

	int cacheSize = 4 * 1024 * 1024; // 4MiB   LruCache<String, Bitmap> bitmapCache = new LruCache<String, Bitmap(cacheSize) {protected int sizeOf(String key, Bitmap value) {           return value.getByteCount();       }   
}

添加缓存

添加缓存调用LRUCahe的put(key,value)方法,put方法先通过sizeof函数计算当前待添加节点所占内存大小,然后将其添加到map中,重新计算当前缓存大小,如果旧节点非空,则调用entryRemove方法通知该旧节点已移除,以方便使用者做清理操作。最后再调用trimToSize()方法检查并移除已超过maxSize的最老的节点。

 public final V put(K key, V value) {//注意 key和value都不能为nullif (key == null || value == null) {throw new NullPointerException("key == null || value == null");}V previous;synchronized (this) {putCount++;size += safeSizeOf(key, value);previous = map.put(key, value);if (previous != null) {size -= safeSizeOf(key, previous);}}if (previous != null) {entryRemoved(false, key, previous, value);}trimToSize(maxSize);return previous;}public 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 = 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);}}

获取缓存

获取缓存调用LRUCache的get方法,其会根据key去map中查找对应值,如果没有,则会调用create方法尝试创建默认值,如果创建了默认值,则会返回默认值,并重新走一遍trimToSize,保证内存缓存大小在maxSize限制内。

public final V get(K key) {if (key == null) {throw new NullPointerException("key == null");}V mapValue;synchronized (this) {mapValue = map.get(key);if (mapValue != null) {hitCount++;return mapValue;}missCount++;}/** Attempt to create a value. This may take a long time, and the map* may be different when create() returns. If a conflicting value was* added to the map while create() was working, we leave that value in* the map and release the created value.*/V createdValue = create(key);if (createdValue == null) {return null;}synchronized (this) {createCount++;mapValue = map.put(key, createdValue);if (mapValue != null) {// There was a conflict so undo that last putmap.put(key, mapValue);} else {size += safeSizeOf(key, createdValue);}}if (mapValue != null) {entryRemoved(false, key, createdValue, mapValue);return mapValue;} else {trimToSize(maxSize);return createdValue;}}

删除缓存

删除缓存主要涉及三件事:

  1. 从map中删除该节点
  2. entryRemove方法通知使用者该节点已被移除
  3. 重新计算当前缓存的大小
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;}

LinkedHashMap如何实现LRU算法

LinkedHashMap继承于HashMap,同时内部自定义了LinkedHashMapEntry节点形成了双向链表结构,其内部有head和tail两个指向头和尾节点的指针,通过accessOrder变量来控制是否顺序。

	static class LinkedHashMapEntry<K,V> extends HashMap.Node<K,V> {LinkedHashMapEntry<K,V> before, after;LinkedHashMapEntry(int hash, K key, V value, Node<K,V> next) {super(hash, key, value, next);}}transient LinkedHashMapEntry<K,V> head;transient LinkedHashMapEntry<K,V> tail;//true表示按照访问顺序,false表示按照插入顺序final boolean accessOrder;

那么如何LinkedHashMap如何更新节点顺序呢?我们看下get方法的流程就知道了。

	public V get(Object key) {Node<K,V> e;//调用父类HashMap的getNode方法获取key对应的节点,该方法会根据hash去对应的桶内查找key相等的节点if ((e = getNode(hash(key), key)) == null)return null;//如果accessOrder=true,则表示要按照访问顺序对节点排序if (accessOrder)afterNodeAccess(e);return e.value;}

get方法做了两件事:

  1. 调用父类HashMap的getNode方法获取key对应的节点
  2. 如果获取到节点值且accessOrder=true,则调用afterNodeAccess方法,该方法会对节点排序

接下来看下afterNodeAccess方法

	void afterNodeAccess(Node<K,V> e) { // move node to lastLinkedHashMapEntry<K,V> last;//如果tail节点不等于当前节点if (accessOrder && (last = tail) != e) {LinkedHashMapEntry<K,V> p =(LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;//将p节点从链表中删除p.after = null;if (b == null)head = a;elseb.after = a;if (a != null)a.before = b;elselast = b;if (last == null)head = p;else {p.before = last;last.after = p;}//更新tail节点指向p,即当前p变成了尾节点,即最新使用的节点,当LRUCache中大小超过maxSize时,会调用eldest方法找到最近最少使用的节点。实际上就是返回head节点tail = p;++modCount;}}public Map.Entry<K, V> eldest() {return head;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/34932.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot的Mybatis-plus实战之扩展功能

文章目录 一、枚举处理器第一步、定义枚举第二步、配置文件中设置 在学习mybatisPlus时会用到扩展功能&#xff0c;极大解放生产力&#xff0c;记录下来&#xff0c;方便备查。 一、枚举处理器 第一步、定义枚举 新建枚举类UserStatusEnum&#xff0c;其主要内容如下所示。 E…

JDBC: 2.初级教程

搭建 依赖 <dependencies><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.27</version></dependency></dependencies> jdbc.properties u…

第10章 启动过程组 (识别干系人)

第10章 启动过程组 10.2识别干系人&#xff0c;在第三版教材第361~362页&#xff1b; 文字图片音频方式 视频13 第一个知识点&#xff1a;主要工具与技术 1、数据收集 问卷调查 包括一对一调查、焦点小组讨论&#xff0c;或其他大规模信息收集技术 头脑风暴 头脑风暴&#xff…

本地服务怎么发布成rpc服务

目录 1.引入 2.user.proto 3.userservice.cc 1.引入 example文件夹作为我们框架项目的使用实例&#xff0c;在example文件夹下创建callee和caller两个文件夹 callee是RPC服务的提供者。在callee创建一个文件&#xff1a;userservice.cc 我们有没有这样一个框架&#xff0c;把…

代码随想录算法训练营Day49|300.最长递增子序列、674.最长连续递增序列、718.最长重复子数组

最长递增子序列 300. 最长递增子序列 - 力扣&#xff08;LeetCode&#xff09; dp[i]为到当前位置i为止的最长递增子序列的长度&#xff0c;所以dp[nums.size()-1]并不一定是整个数组的最长递增子序列的长度。这里需要注意&#xff0c;但这个dp[i]怎么来的&#xff0c;我确实…

基于FreeRTOS+STM32CubeMX+LCD1602+MCP4162(SPI接口)的数字电位器Proteus仿真

一、仿真原理图: 二、仿真效果: 三、STM32CubeMX配置: 1)、SPI配置: 2)、时钟配置: 四、软件部分: 1)、主函数: /* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : mai…

牛皮的程序猿后端返回值怎么定义

在后端接口封装中&#xff0c;我们一般都会对返回的数据做一个封装&#xff0c;以防止系统出现不可预期的数据结构和类型。比如这样&#xff1a; 结构体 1 {"success": true,"code": 200,"message": "成功","data": {&quo…

MinIO下载和安装(Windows)

1、MinIO下载和安装 | 用于创建高性能对象存储的代码和下载内容 2、在本地硬盘中并新建一个minio文件夹 里面再创建bin文件夹和data文件夹 bin 用于存放下载的minio.exe data 用于存放数据 logs 用于存放日志 3、 编写启动脚本start.bat echo off echo [信息] 运行MinIO文服务…

群智优化:探索BP神经网络的最优配置

群智优化&#xff1a;探索BP神经网络的最优配置 一、数据集介绍 鸢尾花数据集最初由Edgar Anderson测量得到&#xff0c;而后在著名的统计学家和生物学家R.A Fisher于1936年发表的文章中被引入到统计和机器学习领域数据集特征&#xff1a; 鸢尾花数据集包含了150个样本&#…

赶紧收藏!2024 年最常见的操作系统面试题(三)

上一篇地址&#xff1a;赶紧收藏&#xff01;2024 年最常见的操作系统面试题&#xff08;二&#xff09;-CSDN博客 五、操作系统中的文件系统是如何工作的&#xff1f; 操作系统中的文件系统是一套用于存储、组织和检索文件的系统。它提供了一种结构化的方式来管理存储设备上…

工业软件的分类与选择策略:针对中小企业的实际应用考量

工业软件是现代工业体系的“大脑”&#xff0c;已经渗透到几乎所有工业领域的核心环节&#xff0c;是现代产业之“魂”&#xff0c;是制造强国之重器。工业软件通过优化生产流程、实时监控设备状态、实现自动化控制等功能&#xff0c;可以帮助企业显著提升生产效率和质量&#…

鸿蒙开发系统基础能力:【@ohos.hiTraceMeter (性能打点)】

性能打点 本模块提供了追踪进程轨迹&#xff0c;度量程序执行性能的打点能力。本模块打点的数据供hiTraceMeter工具分析使用。 说明&#xff1a; 本模块首批接口从API version 8开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。 导入模块 impor…

MySQL配置数据库允许大写字母

MySQL 5.7 默认是支持大写字母的&#xff0c;MySQL 8.0则默认不支持&#xff0c;数据库名称默认都是小写&#xff0c;即使输入了大写也会变成小写&#xff0c;如果你希望数据库名称允许大写字母&#xff0c;你可以修改 MySQL 的配置文件实现此操作&#xff1a; 操作步骤 在My…

【宠粉赠书】SQLServer2022:从入门到精通

为了回馈粉丝们的厚爱&#xff0c;今天小智给大家送上一套数据库学习的必备书籍——《SQL Server 2022从入门到精通》。下面我会详细给大家介绍这套图书&#xff0c;文末留有领取方式。 图书介绍 《SQL Server 2022从入门到精通》系统全面地介绍SQL Server 2022数据库应用与开…

Dolphinscheduler Docker部署全攻略

作者| 陈逸飞 Docker部署的目的是在容器中快速启动部署Apache Dolphinscheduler服务。 先决条件 docker-composedocker 使用容器单机部署Dolphinscheduler 请下载源码包apache-dolphinscheduler--src.tar.gz&#xff0c;下载地址&#xff1a;下载 首先确定服务启动所需的…

记录一个Xshell使用中Xmanager...X11转发的提示问题

希望文章能给到你启发和灵感&#xff5e; 如果觉得有帮助的话&#xff0c;点赞关注收藏支持一下博主哦&#xff5e; 阅读指南 一、环境说明1.1 硬件环境1.2 软件环境 二、问题和错误三、解决四、理解和延伸一下 一、环境说明 考虑环境因素&#xff0c;大家适当的对比自己的软硬…

黑马程序员——Spring框架——day08——maven高级

目录&#xff1a; 分模块开发与设计 分模块开发的意义 问题导入模块拆分原则分模块开发&#xff08;模块拆分&#xff09; 问题导入创建Maven模块书写模块代码通过maven指令安装模块到本地仓库&#xff08;install指令&#xff09;依赖管理 依赖传递 问题导入可选依赖 问题导入…

现在还有人想做TIKTOK吗?

近几年大家都说tiktok不好做了&#xff0c;但为何仍有人愿意投身其中&#xff0c;这背后必然隐藏着巨大的商机。 面对激烈的市场竞争和变化无常的用户需求&#xff0c;我们该如何掌控其中的关键呢&#xff1f; 深入了解目标受众&#xff1a;所谓知己知彼&#xff0c;百战不殆…

国内镜像源网址

腾讯&#xff1a;腾讯软件源 (tencent.com) 阿里&#xff1a;阿里巴巴开源镜像站-OPSX镜像站-阿里云开发者社区 (aliyun.com) 清华&#xff1a;清华大学开源软件镜像站 | Tsinghua Open Source Mirror

数据结构与算法—空间复杂度详解与示例(C#,C++)

文章目录 1. 数据结构概述2. 空间复杂度的定义及影响因素3. 空间复杂度的区分常数空间复杂度&#xff08;O(1)&#xff09;线性空间复杂度&#xff08;O(n)&#xff09;其他空间复杂度 4. 几种典型数据结构的优缺点分析数组&#xff08;Array&#xff09;链表&#xff08;Linke…