Android随笔-LRU 缓存的底层数据结构详解
LRU 缓存的底层数据结构详解一、LRU 是什么LRULeast Recently Used最近最少使用是一种缓存淘汰策略当缓存满时优先淘汰最久没有被访问的数据。访问顺序A → B → C → D → B → A → E 缓存容量 3 步骤 1. 访问 A[A] (A 最近使用) 2. 访问 B[B, A] (B 比 A 新) 3. 访问 C[C, B, A] (满了) 4. 访问 D淘汰 A[D, C, B] (A 最久没用了) 5. 访问 B移到头部[B, D, C] 6. 访问 A淘汰 C[A, B, D] 7. 访问 E淘汰 D[E, A, B]二、LRU 的底层数据结构标准实现HashMap 双向链表LinkedHashMap┌─────────────────────────────────────────┐ │ HashMapK, Node │ │ Key → Node 引用 │ │ 提供 O(1) 的查找能力 │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 双向链表维护访问顺序 │ │ │ │ head ←→ Node1 ←→ Node2 ←→ Node3 ←→ tail│ │ (最近使用) ↑ (最久未用) │ │ 新节点插入头部 │ │ 访问节点移到头部 │ │ 淘汰从尾部移除 │ └─────────────────────────────────────────┘为什么必须结合两者单独使用 HashMap单独使用链表HashMap 链表查找 O(1) ✅查找 O(n) ❌查找 O(1) ✅无法维护顺序 ❌顺序维护方便 ✅顺序维护方便 ✅淘汰需遍历 ❌插入/删除 O(1) ✅插入/删除 O(1) ✅三、双向链表如何提高查询效率关键认知双向链表本身查询是 O(n)// 普通双向链表查找必须遍历Nodefind(Nodehead,intkey){Nodecurhead;while(cur!null){if(cur.keykey)returncur;curcur.next;}returnnull;// O(n)}真正提高查询效率的是 HashMap不是链表。那链表的作用是什么链表负责维护访问顺序和实现 O(1) 的插入/删除/移动。classLRUCache{// HashMapO(1) 查找节点位置privateHashMapInteger,Nodemap;// 双向链表维护访问顺序O(1) 调整位置privateNodehead,tail;classNode{intkey,value;Nodeprev,next;// 双向指针O(1) 删除和插入}}为什么是双向链表而不是单向链表操作单向链表双向链表头部插入O(1) ✅O(1) ✅头部删除O(1) ✅O(1) ✅尾部删除O(n)❌O(1)✅已知节点删除O(n)❌O(1)✅节点前移O(n) ❌O(1) ✅LRU 的核心操作访问节点 → 移到头部 → 需要删除原位置 插入头部缓存满 → 淘汰尾部 → 需要删除尾部这两个操作在单向链表中都需要遍历找到前驱节点是 O(n)。双向链表通过prev指针直接定位前驱是 O(1)。四、完整 Java 实现publicclassLRUCacheK,V{// 双向链表节点privateclassNode{Kkey;Vvalue;Nodeprev,next;Node(Kkey,Vvalue){this.keykey;this.valuevalue;}}privatefinalintcapacity;privatefinalHashMapK,Nodemap;// O(1) 查找privatefinalNodehead,tail;// 虚拟头尾节点简化边界处理publicLRUCache(intcapacity){this.capacitycapacity;this.mapnewHashMap();// 虚拟节点不存数据headnewNode(null,null);tailnewNode(null,null);head.nexttail;tail.prevhead;}// O(1) 查询publicVget(Kkey){Nodenodemap.get(key);if(nodenull)returnnull;// 移到头部最近使用moveToHead(node);returnnode.value;}// O(1) 插入/更新publicvoidput(Kkey,Vvalue){Nodenodemap.get(key);if(node!null){// 更新已有节点node.valuevalue;moveToHead(node);}else{// 新建节点NodenewNodenewNode(key,value);map.put(key,newNode);addToHead(newNode);// 超出容量淘汰尾部if(map.size()capacity){NoderemovedremoveTail();map.remove(removed.key);}}}// O(1) 移到头部privatevoidmoveToHead(Nodenode){removeNode(node);// 先删除原位置addToHead(node);// 再插入头部}// O(1) 删除节点双向链表的核心优势privatevoidremoveNode(Nodenode){node.prev.nextnode.next;node.next.prevnode.prev;}// O(1) 插入头部privatevoidaddToHead(Nodenode){node.prevhead;node.nexthead.next;head.next.prevnode;head.nextnode;}// O(1) 删除尾部双向链表的核心优势privateNoderemoveTail(){Nodenodetail.prev;// 直接通过 prev 找到尾部前驱removeNode(node);returnnode;}}五、LinkedHashMap 实现Java 内置Java 的LinkedHashMap已经内置了 LRU 功能只需重写removeEldestEntrypublicclassLRUCacheK,VextendsLinkedHashMapK,V{privatefinalintcapacity;publicLRUCache(intcapacity){// accessOrder true按访问顺序排序false 是按插入顺序super(capacity,0.75f,true);this.capacitycapacity;}OverrideprotectedbooleanremoveEldestEntry(Map.EntryK,Veldest){// 当容量超过时自动移除最老的条目returnsize()capacity;}// 使用// LRUCacheString, Bitmap cache new LRUCache(100);// cache.put(key, bitmap);// Bitmap bmp cache.get(key); // 访问后自动移到头部}LinkedHashMap 内部结构HashMap 部分 table[0] → Entry1 → Entry2 table[1] → Entry3 table[2] → null ... 双向链表部分维护顺序 header ←→ Entry3 ←→ Entry1 ←→ Entry2 (最近使用) (最久未用)六、Android 中的 LRU 应用1. LruCacheAndroid 内置// 内存缓存用于图片等intmaxMemory(int)(Runtime.getRuntime().maxMemory()/1024);intcacheSizemaxMemory/8;// 取 1/8LruCacheString,BitmapbitmapCachenewLruCacheString,Bitmap(cacheSize){OverrideprotectedintsizeOf(Stringkey,Bitmapvalue){// 返回每个条目的大小KBreturnvalue.getByteCount()/1024;}OverrideprotectedvoidentryRemoved(booleanevicted,Stringkey,BitmapoldValue,BitmapnewValue){// 条目被移除时的回调可以回收 Bitmapif(oldValue!null!oldValue.isRecycled()){oldValue.recycle();}}};// 使用bitmapCache.put(url,bitmap);BitmapcachedbitmapCache.get(url);2. Glide 的缓存结构Glide 三级缓存 1. 活动资源缓存ActiveResources - 弱引用 HashMap正在显示的图片 - 不在 LRU 中避免被回收 2. 内存缓存LruResourceCache - LruCache最近使用的图片 - 容量屏幕尺寸 × 2.5 3. 磁盘缓存DiskLruCache - 基于文件的 LRU 缓存 - 默认 250MB七、总结问题答案LRU 底层数据结构HashMap 双向链表HashMap 的作用O(1) 查找节点双向链表的作用O(1) 维护访问顺序插入、删除、移动为什么不用单向链表删除尾部和已知节点需要 O(n) 找前驱为什么不用数组插入/删除需要 O(n) 移动元素为什么不用红黑树不需要排序链表即可维护顺序且常数更小查询效率谁贡献的HashMap 贡献 O(1) 查找链表只负责顺序维护一句话HashMap 提供找到它在哪双向链表提供快速调整位置。两者缺一不可。

相关新闻

Android—Jetpack教程(二):ViewModel与LiveData实战解析

Android—Jetpack教程(二):ViewModel与LiveData实战解析

1. ViewModel与LiveData为何成为Android开发标配每次屏幕旋转导致Activity重建时,数据丢失的烦恼还记得吗?三年前我维护的一个电商应用就因此频繁出现空指针崩溃。直到Google推出ViewModel和LiveData这对黄金组合,这类问题才迎刃而解。现在它…

2026/7/16 2:06:01阅读更多 →
2026年西湖区大学生学习工具怎么选4个实用选择标准帮你把关

2026年西湖区大学生学习工具怎么选4个实用选择标准帮你把关

"2026年西湖区大学生选学习工具,核心是匹配课堂复习、论文调研、知识自测这些日常校园场景,不用追热门噱头或者贵得离谱的付费工具,记住四个实用选择标准就能帮你把关,避开同学盲安利踩的坑,接下来我会把标准拆解…

2026/7/16 2:01:01阅读更多 →
2026高校教师应对讲座网课的录课整理使用场景实用指南

2026高校教师应对讲座网课的录课整理使用场景实用指南

"针对2026年高校讲座、网课录课整理环节普遍存在的人工效率低、专业术语转写误差大、校本资源沉淀合规性不足等问题,本指南面向高校信息化管理者、IT负责人、采购对接人,梳理录课整理场景的落地路径、选型标准与合规方案,可直接适配高校…

2026/7/16 2:01:01阅读更多 →
【CubeMX+Keil】从零到一:STM32CubeMX配置与Keil MDK工程构建全流程解析

【CubeMX+Keil】从零到一:STM32CubeMX配置与Keil MDK工程构建全流程解析

1. 环境准备与软件安装第一次接触STM32开发的朋友可能会被各种工具链搞得头晕眼花。别担心,我这里会手把手带你完成开发环境的搭建。STM32CubeMX和Keil MDK是ST官方推荐的黄金组合,一个负责硬件配置,一个负责代码编写和调试,配合起…

2026/7/16 3:06:05阅读更多 →
三个维度,三款图表工具

三个维度,三款图表工具

不同场景,匹配不同工具。前言数据可视化工具很多,关键是根据自己的实际场景来选择。本文从三个不同维度切入,分别介绍三款有代表性的工具,供大家参考。一、一站式AI办公角度:百度文库(一)产品定…

2026/7/16 3:06:05阅读更多 →
tensormsg:终极ROS消息与PyTorch张量转换工具完全指南

tensormsg:终极ROS消息与PyTorch张量转换工具完全指南

tensormsg:终极ROS消息与PyTorch张量转换工具完全指南 【免费下载链接】tensormsg The code for mutual conversion between ROS msg and PyTorch tensor used by IB-Robot serves to decouple lerobot itself from ROS code. 项目地址: https://gitcode.com/open…

2026/7/16 3:06:05阅读更多 →
ROS赋能OpenEuler:yocto-meta-ros项目终极入门指南

ROS赋能OpenEuler:yocto-meta-ros项目终极入门指南

ROS赋能OpenEuler:yocto-meta-ros项目终极入门指南 【免费下载链接】yocto-meta-ros ROS (Robot Operating System) support layer 项目地址: https://gitcode.com/openeuler/yocto-meta-ros 前往项目官网免费下载:https://ar.openeuler.org/ar/ …

2026/7/16 3:06:05阅读更多 →
Rust 错误恢复模式:什么时候重试,什么时候快速失败,什么时候降级

Rust 错误恢复模式:什么时候重试,什么时候快速失败,什么时候降级

Rust 错误恢复模式:什么时候重试,什么时候快速失败,什么时候降级 痛定思痛之后,我花了几天时间系统梳理了错误恢复这件事,总结出了一个决策框架。今天分享给大家:在 Rust 里,错误恢复不是非黑即…

2026/7/16 3:06:05阅读更多 →
Claude Code基本操作

Claude Code基本操作

一、读取文件与安全边界 在一个空白的工作目录中,复制粘贴几个文件,然后使用Claude Code进行读取,询问“当前目录下有哪些文件?”。如图所示 : 接下来,询问“读取基本信息”,会得到&#xff1a…

2026/7/16 3:01:05阅读更多 →
VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异

VSCode TypeScript 环境配置对比:全局安装 vs 项目本地安装的4个关键差异当你在VSCode中启动一个新的TypeScript项目时,第一个技术决策往往从安装方式开始。这个看似简单的选择——全局安装还是项目本地安装——实际上会深刻影响你的开发流程、团队协作和…

2026/7/15 6:42:19阅读更多 →
智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手

智慧树刷课插件:5分钟实现自动化学习的智能助手 【免费下载链接】zhihuishu 智慧树刷课插件,自动播放下一集、1.5倍速度、无声 项目地址: https://gitcode.com/gh_mirrors/zh/zhihuishu 智慧树刷课插件是一款专为智慧树在线教育平台设计的Chrome浏…

2026/7/15 6:12:45阅读更多 →
Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案

Steam创意工坊下载器WorkshopDL:跨平台游戏模组获取的终极解决方案 【免费下载链接】WorkshopDL WorkshopDL - The Best Steam Workshop Downloader 项目地址: https://gitcode.com/gh_mirrors/wo/WorkshopDL 你是否在GOG或Epic Games Store购买了心仪的游戏…

2026/7/15 10:54:00阅读更多 →
A--10 Codex Review与GitHub PR工作流实战指南:从代码审查到安全合并

A--10 Codex Review与GitHub PR工作流实战指南:从代码审查到安全合并

摘要:本文系统讲解如何利用Codex App的Review功能与GitHub PR工作流,实现从代码修改到安全合并的完整流程。涵盖Review面板深度使用、/review命令实战、GitHub Connector配置、PR描述撰写技巧,以及常见问题排查方法。通过多个实战案例和流程图,帮助开发者建立高效的AI辅助代…

2026/7/16 0:00:38阅读更多 →
遗传算法解5皇后问题:从Hello World到工业优化的进化实验室

遗传算法解5皇后问题:从Hello World到工业优化的进化实验室

1. 项目概述:为什么用遗传算法解5皇后问题,而不是直接回溯?我带过十几届算法课,也给不少初创团队做过AI架构咨询。每次讲到组合优化问题,学生和工程师的第一反应永远是“写个回溯试试”。这没错——55棋盘上找所有合法…

2026/7/16 0:00:38阅读更多 →
5.1V稳压管输出为何只有4.7V?工作电流与负载影响分析

5.1V稳压管输出为何只有4.7V?工作电流与负载影响分析

前几天调试一个简单的电源模块,用到了5.1V稳压管。电路接好,上电测试,万用表一量——输出居然只有4.7V。第一反应是稳压管坏了,换了一个新的,结果还是4.7V。这让我想起很多初学者都会遇到的困惑:明明标称5.…

2026/7/16 0:00:38阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/15 15:50:47阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/15 8:52:38阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/15 14:06:23阅读更多 →