【面试场景题】通过LinkedHashMap来实现LRU与LFU

文章目录

      • 一、LRU与LFU的概念
        • 1. LRU(Least Recently Used,最近最少使用)
        • 2. LFU(Least Frequently Used,最不经常使用)
      • 二、LinkedHashMap的特性
      • 三、用LinkedHashMap实现LRU
        • 实现代码:
        • 原理说明:
      • 四、用LinkedHashMap实现LFU
        • 实现代码:
        • 原理说明:
      • 总结

一、LRU与LFU的概念

1. LRU(Least Recently Used,最近最少使用)

LRU是一种缓存淘汰策略,核心思想是:当缓存空间满时,优先淘汰最久未被访问的元素

  • 基于“最近使用的元素在未来更可能被再次使用”的假设。
  • 例如:缓存容量为3,访问顺序为A→B→C→A→D,当加入D时缓存满,最久未被访问的是B,因此淘汰B。
2. LFU(Least Frequently Used,最不经常使用)

LFU是另一种缓存淘汰策略,核心思想是:当缓存空间满时,优先淘汰访问次数最少的元素;若访问次数相同,则淘汰最久未被访问的元素

  • 基于“使用频率高的元素在未来更可能被再次使用”的假设。
  • 例如:缓存容量为3,访问次数:A(3次)、B(2次)、C(2次)(C比B更久未访问),加入D时,B和C次数最少,淘汰更久未访问的C。

二、LinkedHashMap的特性

LinkedHashMapHashMap 的子类,其核心特性是维护了一个双向链表,记录Entry的插入顺序或访问顺序,这为实现LRU提供了天然支持。

  • 构造函数参数 accessOrdertrue 表示按访问顺序排序(每次get/put操作会将元素移到链表末尾);false(默认)表示按插入顺序排序。
  • 方法 removeEldestEntry(Map.Entry<K,V> eldest):当此方法返回 true 时,LinkedHashMap 会自动删除最老的Entry(链表头部元素)。

三、用LinkedHashMap实现LRU

利用 LinkedHashMapaccessOrder=true 特性(访问顺序排序),配合重写 removeEldestEntry 方法(当容量超限则删除最老元素),即可实现LRU。

实现代码:
import java.util.LinkedHashMap;
import java.util.Map;public class LRUCache<K, V> extends LinkedHashMap<K, V> {private final int capacity; // 缓存容量// 构造函数:指定容量、负载因子、访问顺序模式public LRUCache(int capacity) {super(capacity, 0.75f, true); // accessOrder=true:按访问顺序排序this.capacity = capacity;}// 重写此方法:当Entry数量超过容量时,返回true,自动删除最老元素@Overrideprotected boolean removeEldestEntry(Map.Entry<K, V> eldest) {return size() > capacity;}// 测试public static void main(String[] args) {LRUCache<Integer, String> cache = new LRUCache<>(3);cache.put(1, "A");cache.put(2, "B");cache.put(3, "C");System.out.println(cache); // {1=A, 2=B, 3=C}(插入顺序)cache.get(1); // 访问1,移到末尾System.out.println(cache); // {2=B, 3=C, 1=A}cache.put(4, "D"); // 容量超限,删除最老元素2System.out.println(cache); // {3=C, 1=A, 4=D}}
}
原理说明:
  • accessOrder=true 确保每次访问(getput)的元素被移到链表末尾(成为“最新”元素)。
  • 链表头部始终是“最久未被访问”的元素,当 size() > capacity 时,removeEldestEntry 返回 true,自动删除头部元素,实现LRU淘汰。

四、用LinkedHashMap实现LFU

LFU需要跟踪元素的访问次数,以及同次数下的最近访问时间LinkedHashMap 本身不直接支持频率排序,需结合额外结构辅助实现:

  1. Map<K, Integer> 记录每个key的访问次数(频率)。
  2. Map<Integer, LinkedHashMap<K, V>> 按频率分组:key为频率,value为该频率下的元素(按访问顺序排序,用于同频率下淘汰最久未访问元素)。
  3. 维护一个变量记录当前最小频率,方便快速找到需淘汰的组。
实现代码:
import java.util.*;public class LFUCache<K, V> {private final int capacity; // 缓存容量private final Map<K, Integer> keyToFreq; // 记录key的访问次数private final Map<Integer, LinkedHashMap<K, V>> freqToEntries; // 按频率分组,每组内按访问顺序排序private int minFreq; // 当前最小频率public LFUCache(int capacity) {this.capacity = capacity;this.keyToFreq = new HashMap<>();this.freqToEntries = new HashMap<>();this.minFreq = 0;}// 获取元素public V get(K key) {if (!keyToFreq.containsKey(key)) {return null;}// 1. 增加访问频率int oldFreq = keyToFreq.get(key);int newFreq = oldFreq + 1;keyToFreq.put(key, newFreq);// 2. 从旧频率组中移除,加入新频率组LinkedHashMap<K, V> oldEntries = freqToEntries.get(oldFreq);V value = oldEntries.remove(key);// 若旧频率组为空,且是最小频率,更新minFreqif (oldEntries.isEmpty()) {freqToEntries.remove(oldFreq);if (oldFreq == minFreq) {minFreq = newFreq;}}// 新频率组不存在则创建(按访问顺序排序)freqToEntries.computeIfAbsent(newFreq, k -> new LinkedHashMap<>(capacity, 0.75f, true)).put(key, value);return value;}// 放入元素public void put(K key, V value) {if (capacity <= 0) return;// 若key已存在,先更新值并触发get(更新频率)if (keyToFreq.containsKey(key)) {freqToEntries.get(keyToFreq.get(key)).put(key, value); // 更新值get(key); // 触发频率更新return;}// 若缓存满,淘汰最不经常使用的元素(最小频率组中最老的)if (keyToFreq.size() >= capacity) {LinkedHashMap<K, V> minFreqEntries = freqToEntries.get(minFreq);// 移除最小频率组中最老的元素(链表头部)K eldestKey = minFreqEntries.keySet().iterator().next();minFreqEntries.remove(eldestKey);keyToFreq.remove(eldestKey);// 若最小频率组为空,移除该组if (minFreqEntries.isEmpty()) {freqToEntries.remove(minFreq);}}// 新增元素:频率为1,加入频率1的组int newFreq = 1;keyToFreq.put(key, newFreq);freqToEntries.computeIfAbsent(newFreq, k -> new LinkedHashMap<>(capacity, 0.75f, true)).put(key, value);minFreq = newFreq; // 新元素频率为1,最小频率更新为1}// 测试public static void main(String[] args) {LFUCache<Integer, String> cache = new LFUCache<>(3);cache.put(1, "A"); // 频率:1→{1:A},min=1cache.put(2, "B"); // 频率:1→{1:A,2:B},min=1cache.put(3, "C"); // 频率:1→{1:A,2:B,3:C},min=1cache.get(1); // 1的频率变为2,min仍为1cache.get(1); // 1的频率变为3,min仍为1cache.put(4, "D"); // 缓存满,淘汰min=1中最老的2(1和2都在频率1组,2更早未访问)System.out.println(cache.get(2)); // null(已被淘汰)System.out.println(cache.get(1)); // A(存在)}
}
原理说明:
  • 频率跟踪keyToFreq 记录每个key的访问次数,每次 get/put 会更新频率。
  • 分组管理freqToEntries 按频率分组,每组用 LinkedHashMapaccessOrder=true)记录元素,确保同频率下最久未访问的元素在链表头部,便于淘汰。
  • 淘汰逻辑:当缓存满时,找到最小频率 minFreq,从对应组中移除最老元素(链表头部),若组为空则移除该组并更新 minFreq

总结

策略核心逻辑LinkedHashMap的作用实现复杂度
LRU淘汰最久未访问元素利用 accessOrder=true 维护访问顺序,重写 removeEldestEntry 实现自动淘汰简单
LFU淘汰访问次数最少(同次数则最久未访问)的元素作为频率分组内的容器,维护同频率元素的访问顺序较复杂(需额外结构跟踪频率)

LRU实现简单、性能好,适合大多数场景;LFU更精准但实现复杂,适合对访问频率敏感的场景。

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

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

相关文章

第5章 Excel公式与函数应用指南(2):数学函数

5.2 数学函数 Excel作为强大的数据处理工具,其内置的数学函数体系为用户提供了丰富的计算能力。从基础的四则运算到复杂的指数对数计算,从简单的数值舍入到专业的矩阵运算,Excel的数学函数几乎可以满足各类计算需求。 本节将重点为您解析七个常用且实用的数学函数:求和函…

mysql复制连接下的所有表+一次性拷贝到自己的库

1.导出链接下的所有数据mysqldump -h 地址 -u 数据库名 -p --all-databases --single-transaction --master-data2 > all_dbs.sql2.导入自己的库mysql -h 127.0.0.1 -u root -p < all_dbs.sql3.指定导出某些库mysqldump -u root -p --databases db1 db2 db3 > /path/t…

开发手札:UnrealEngine和Unity3d坐标系问题

最近把一套网络模块和一套组件模块从u3d改造到ue4。网络模块通用性很高&#xff0c;毕竟协议都是通用网络协议&#xff0c;改造后没啥问题。但是改造组件模块的时候就遇到了问题。首先&#xff0c;unity3d的坐标系是标准左手坐标系&#xff0c;如下&#xff1a;同时自己的几何算…

QML 鼠标穿透

事件&#xff1a; 有一个输入框(TextField)&#xff0c;需要实现鼠标悬浮时改变边框颜色&#xff0c;鼠标移出后恢复原来边框颜色&#xff1b; 这时如果需要实现此功能&#xff0c;就得使用到MouseArea&#xff0c;鼠标操作区域填充满整个TextField。 然后实现鼠标移入移入出的…

VR 设备 PCB 怎样凭借高频材料达成高速传输

VR 设备的沉浸式体验依赖于高分辨率图像与低延迟交互&#xff0c;这要求设备内部数据传输速率达到 10Gbps 以上&#xff0c;而印制线路板&#xff08;PCB&#xff09;作为信号传输的核心载体&#xff0c;其材料性能直接决定传输效率。高频材料凭借低介电常数&#xff08;Dk&…

Oracle字段操作

1. 新增字段 -- 新增字段 ALTER TABLE MES.WT_SUPPLEMENT_RECORD ADD (PAR_ATTR3 NUMBER DEFAULT NULL);2. 修改字段类型 -- 修改字段类型 ALTER TABLE MES.WT_SUPPLEMENT_RECORD MODIFY (PAR_ATTR3 VARCHAR2(32));3. 删除字段 -- 删除字段 ALTER TABLE MES.WT_SUPPLEMENT_RECO…

【原创】基于 Flask 的简单文件收集器

在单位内网环境中&#xff0c;我经常需要收集 pdf 格式的记录表。于是我基于 ai ide&#xff0c;开发了一个基于 Flask 开发的轻量级文件上传服务项目&#xff0c;部署在单位飞腾芯的银河麒麟系统上&#xff08;当然由于 python 的跨平台&#xff0c;在 windows 和 mac 上也可部…

学习Java的Day28

今天在昨天完成的留言板项目基础上&#xff0c;我进一步开发了一个酒店房型管理系统。该系统采用MVC架构&#xff0c;主要功能是对酒店房型信息进行增删改查操作。数据库设计方面&#xff0c;我创建了hotel_room_type表&#xff0c;包含以下字段&#xff1a;id&#xff1a;主键…

Leetcode——556. 下一个更大元素 III

题目链接&#xff1a;556. 下一个更大元素 III &#xff08;由于图片上传失败&#xff0c;不贴原题目了&#xff0c;有需要可以前往力扣查看&#xff09; 本文给出该题的单调栈做法&#xff0c;同时绕过所有库函数&#xff0c;所有逻辑均自行实现。 本题的思路就是从右向左按…

Idea打包可执行jar,MANIFEST.MF文件没有Main-Class属性:找不到或无法加载主类

背景&#xff1a;IDEA传统方法【Project structure】-->artifact---->build的模式&#xff0c;打包【Maven】项目&#xff0c;发现生成的可执行jar包&#xff0c;显示【找不到或无法加载主类】。但是用【Maven】的Assembly可以正常生成。期望用传统方法实现打jar包方法&a…

检索增强生成:RAG(Retrieval Augmented Generation)

什么是 RAG&#xff1f;为什么使用 RAG&#xff1f;LLM 微调 和 RAG&#xff1f;实战什么是 RAG&#xff1f; RAG 在论文《Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks》中被引入&#xff0c;原论文是这样描述的&#xff1a; 探索了一种 通用的 检索增…

Android 设置/修改系统NTP服务地址

Android 手机的 NTP 时间同步&#xff08;网络时间同步&#xff09;主要依赖网络&#xff0c;但系统时间来源还包括其他方式&#xff0c;整体时间校准机制是多种来源的结合。具体可分为以下几类&#xff1a; 1. 网络 NTP 同步&#xff08;最主要方式&#xff09; 这是 Androi…

Ubuntu22.04 安装vitis2023.2 卡在“Generating installed device list“.

关于这个问题&#xff0c;xilinx有官方说明&#xff0c;链接 原因&#xff1a;问题是 Ubuntu 20.04 缺少 libtinfo.so.5 库。 解决办法&#xff1a; sudo apt-get install libtinfo5

前端全栈修炼手册:从 Vue3 到工程化的进阶之路

本文将全方位覆盖前端开发的核心知识&#xff0c;从 Vue3 框架的基础语法到复杂的工程化实践&#xff0c;从包管理工具的使用到模块规范的深入理解&#xff0c;带你踏上从入门到精通的进阶之路。 Vue3 框架&#xff1a;新时代前端开发的基石 Vue3 核心语法探秘 Vue3 作为目前…

Jetpack Compose 常用控件

Jetpack Compose 常用控件一、基础展示控件&#xff1a;呈现静态内容二、交互控件&#xff1a;响应用户操作三、列表与网格控件&#xff1a;展示大量数据四、导航与标签控件&#xff1a;组织页面结构五、反馈控件&#xff1a;提示与加载状态六、布局控件&#xff1a;组织 UI 结…

Android适配最新SplashScreen方案:让启动页不再“翻车“

Android适配最新SplashScreen方案:让启动页不再"翻车" 各位开发者大佬们,最近是不是又被Android的SplashScreen适配搞得焦头烂额?别慌,今天咱们就来聊聊这个让人又爱又恨的启动页适配方案,保证让你笑出腹肌的同时,还能把技术要点牢牢掌握![6][7][9][10] 一、…

【自动驾驶】《Sparse4Dv3》代码学习笔记

这里时间比较有限&#xff0c;优先看Sparse4Dv3方法里面相对以前改动的地方。 0.参考 代码v1/v2/v3:https://github.com/HorizonRobotics/Sparse4D 跑起来&#xff1a;https://github.com/HorizonRobotics/Sparse4D/blob/v3.0/docs/quick_start.md 1.方法 &#xff08;1&a…

「ECG信号处理——(22)Pan-Tompkins Findpeak 阈值检测 差分阈值算法——三种R波检测算法对比分析」2025年8月8日

目录 1、引言 2、算法原理 &#xff08;1&#xff09;Pan-Tompkins 算法&#xff08;方法1&#xff09; &#xff08;2&#xff09;Findpeak 阈值检测算法&#xff08;方法2&#xff09; &#xff08;3&#xff09;差分阈值算法&#xff08;方法3&#xff09; 3、算法性能…

Qdrant Filtering:must / should / must_not 全解析(含 Python 实操)

在向量搜索中&#xff0c;过滤&#xff08;Filtering&#xff09; 是保证结果精准性和业务契合度的关键手段。Qdrant 的过滤机制不仅能在向量相似度检索的基础上叠加结构化条件&#xff0c;还提供了灵活的布尔逻辑组合&#xff0c;让我们可以像写数据库查询一样&#xff0c;精准…

五、RuoYi-Cloud-Plus 前端项目部署以及如何改后端请求地址。

1.前情描述 前面的文章我们介绍了RuoYi-Cloud-Plus的nocos的配置内容&#xff0c;已经启动其他服务要注意什么东西。 专栏内容在这&#xff0c;感兴趣可以看看。 https://blog.csdn.net/weixin_42868605/category_13023920.html 2.前端项目部署。 官网地址&#xff1a;plus…