LinkedHashMap与LRU缓存

序、慢慢来才是最快的方法。

背景

LinkedHashMap 是继承于 HashMap 实现的哈希链表,它同时具备双向链表和散列表的特点。事实上,LinkedHashMap 继承了 HashMap 的主要功能,并通过 HashMap 预留的 Hook 点维护双向链表的逻辑。

1.缓存淘汰算法

1.1什么是缓存淘汰算法

缓存是提高数据读取性能的通用技术,在硬件和软件设计中被广泛使用,例如CPU缓存,Glide缓存内存缓存,数据库缓存。由于缓存空间不可能无限大,当缓存容量占满时,就需要利用某种策略将部分数据换出缓存,这就是缓存的淘汰问题/替换策略。

常见的缓存淘汰策略有:

  • 1、随机策略: 使用一个随机数生成器随机地选择要被淘汰的数据块;

  • 2、FIFO 先进先出策略: 记录各个数据块的访问时间,最早访问的数据最先被淘汰;

  • 3、LRU (Least Recently Used)最近最少策略: 记录各个数据块的访问 “时间戳” ,最近最久未使用的数据最先被淘汰。与前 2 种策略相比,LRU 策略平均缓存命中率更高,这是因为 LRU 策略利用了 “局部性原理”:最近被访问过的数据,将来被访问的几率较大,最近很久未访问的数据,将来访问的几率也较小;

  • 4、LFU (Least Frequently Used)最不经常使用策略: 与 LRU 相比,LFU 更加注重使用的 “频率” 。LFU 会记录每个数据块的访问次数,最少访问次数的数据最先被淘汰。但是有些数据在开始时使用次数很高,以后不再使用,这些数据就会长时间污染缓存。可以定期将计数器右移一位,形成指数衰减。

FIFO和LRU策略图形解释

1.2向外看,LRU的变型

在标准的LRU算法上还有一些变型实现,这是因为LRU算法本身也存在一些不足。例如,当数据中热点数据较多时,LRU能够保证较多的命中率。但是当有偶然的批量的非热点数据产生时,就会讲热点数据寄出缓存,是的缓存被污染。因此,LRU也有一些变型。

  • LRU-K: 提供两个 LRU 队列,一个是访问计数队列,一个是标准的 LRU 队列,两个队列都按照 LRU 规则淘汰数据。当访问一个数据时,数据先进入访问计数队列,当数据访问次数超过 K 次后,才会进入标准 LRU 队列。标准的 LRU 算法相当于 LRU-1;
  • Two Queue: 相当于 LRU-2 的变型,将访问计数队列替换为 FIFO 队列淘汰数据数据。当访问一个数据时,数据先进入 FIFO 队列,当第 2 次访问数据时,才会进入标准 LRU 队列;
  • Multi Queue: 在 LRU-K 的基础上增加更多队列,提供多个级别的缓冲。

1.3如何实现LRU缓存淘汰算法

我们尝试找到 LRU 缓存淘汰算法的实现方案。经过总结,我们可以定义一个缓存系统的基本操作:

我们发现,前 3 个操作都有 “查询” 操作, 所以缓存系统的性能主要取决于查找数据和淘汰数据是否高效。 下面,我们用递推的思路推导 LRU 缓存的实现方案,主要分为 3 种方案:

PS:双向链表+散列表这种数据结构就叫“哈希链表或链式哈希表”,Java的LinkedHashMap就是基于哈希链表的数据结构。

2.LinkedHashMap哈希链表

PS:需要注意:LinkedHashMap 中的 “Linked” 实际上是指双向链表,并不是指解决散列冲突中的分离链表法。

2.1LinkedHashMap 的特点

2.2说一下 HashMap 和 LinkedHashMap 的区别?

    • 操作 1 - 添加数据: 先查询数据是否存在,不存在则添加数据,存在则更新数据,并尝试淘汰数据;
    • 操作 2 - 删除数据: 先查询数据是否存在,存在则删除数据;
    • 操作 3 - 查询数据: 如果数据不存在则返回 null;
    • 操作 4 - 淘汰数据: 添加数据时如果容量已满,则根据缓存淘汰策略一个数据。
  • 方案 1 - 基于时间戳的数组: 在每个数据块中记录最近访问的时间戳,当数据被访问(添加、更新或查询)时,将数据的时间戳更新到当前时间。当数组空间已满时,则扫描数组淘汰时间戳最小的数据。

    • 查找数据: 需要遍历整个数组找到目标数据,时间复杂度为 O(n);
    • 淘汰数据: 需要遍历整个数组找到时间戳最小的数据,且在移除数组元素时需要搬运数据,整体时间复杂度为 O(n)。
  • 方案 2 - 基于双向链表: 不再直接维护时间戳,而是利用链表的顺序隐式维护时间戳的先后顺序。当数据被访问(添加、更新或查询)时,将数据插入到链表头部。当空间已满时,直接淘汰链表的尾节点。

    • 查询数据:需要遍历整个链表找到目标数据,时间复杂度为 O(n);
    • 淘汰数据:直接淘汰链表尾节点,时间复杂度为 O(1)。
  • 方案 3 - 基于双向链表 + 散列表: 使用双向链表可以将淘汰数据的时间复杂度降低为 O(1),但是查询数据的时间复杂度还是 O(n),我们可以在双向链表的基础上增加散列表,将查询操作的时间复杂度降低为 O(1)。

    • 查询数据:通过散列表定位数据,时间复杂度为 O(1);
    • 淘汰数据:直接淘汰链表尾节点,时间复杂度为 O(1)。
  • 1、LinkedHashMap 是继承于 HashMap 实现的哈希链表,它同时具备双向链表和散列表的特点。事实上,LinkedHashMap 继承了 HashMap 的主要功能,并通过 HashMap 预留的 Hook 点维护双向链表的逻辑。

    • 1.1 当 LinkedHashMap 作为散列表时,主要体现出 O(1) 时间复杂度的查询效率;
    • 1.2 当 LinkedHashMap 作为双向链表时,主要体现出有序的特性。
  • 2、LinkedHashMap 支持 2 种排序模式,这是通过构造器参数 accessOrder 标记位控制的,表示是否按照访问顺序排序,默认为 false 按照插入顺序。

    • 2.1 插入顺序(默认): 按照数据添加到 LinkedHashMap 的顺序排序,即 FIFO 策略;
    • 2.2 访问顺序: 按照数据被访问(包括插入、更新、查询)的顺序排序,即 LRU 策略。
  • 3、在有序性的基础上,LinkedHashMap 提供了维护了淘汰数据能力,并开放了淘汰判断的接口 removeEldestEntry()。在每次添加数据时,会回调 removeEldestEntry() 接口,开发者可以重写这个接口决定是否移除最早的节点(在 FIFO 策略中是最早添加的节点,在 LRU 策略中是最早未访问的节点);

    • 4、与 HashMap 相同,LinkedHashMap 也不考虑线程同步,也会存在线程安全问题。可以使用 Collections.synchronizedMap 包装类,其原理也是在所有方法上增加 synchronized 关键字。

事实上,HashMap 和 LinkedHashMap 并不是平行的关系,而是继承的关系,LinkedHashMap 是继承于 HashMap 实现的哈希链表。

两者主要的区别在于有序性: LinkedHashMap 会维护数据的插入顺序或访问顺序,而且封装了淘汰数据的能力。在迭代器遍历时,HashMap 会按照数组顺序遍历桶节点,从开发者的视角看是无序的。而是按照双向链表的顺序从 head 节点开始遍历,从开发者的视角是可以感知到的插入顺序或访问顺序。

LinkedHashMap 继承于 HashMap,在后者的基础上通过双向链表维护节点的插入顺序或访问顺序。因此,我们先回顾下 HashMap 为 LinkedHashMap 预留的 Hook 点:

参考

  • afterNodeAccess: 在节点被访问时回调;
  • afterNodeInsertion: 在节点被插入时回调,其中有参数 evict 标记是否淘汰最早的节点。在初始化、反序列化或克隆等构造过程中,evict 默认为 false,表示在构造过程中不淘汰。 afterNodeRemoval: 在节点被移除时回调。

Java & Android 集合框架 #7 如何使用 LinkedHashMap 实现 LRU 缓存? - 掘金

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

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

相关文章

车辆车型识别系统python+TensorFlow+Django网页界面+算法模型

一、介绍 车辆车型识别系统。本系统使用Python作为主要开发编程语言,通过TensorFlow搭建算法模型网络对收集到的多种车辆车型图片数据集进行训练,最后得到一个识别精度较高的模型文件。并基于该模型搭建Django框架的WEB网页端可视化操作界面。实现用户上…

【Unity基础】6.动画状态机

【Unity基础】6.动画状态机 大家好,我是Lampard~~ 欢迎来到Unity基础系列博客,所学知识来自B站阿发老师~感谢 (一)Animator Controller组件 (1)创建组件 Animator Controller组件是unity用于控制管…

机器学习-无监督算法之降维

降维:将训练数据中的样本从高维空间转换到低维空间,降维是对原始数据线性变换实现的。为什么要降维?高维计算难,泛化能力差,防止维数灾难优点:减少冗余特征,方便数据可视化,减少内存…

主动配电网故障恢复的重构与孤岛划分matlab程序

微❤关注“电气仔推送”获得资料(专享优惠) 参考文档: A New Model for Resilient Distribution Systems by Microgrids Formation; 主动配电网故障恢复的重构与孤岛划分统一模型; 同时考虑孤岛与重构的配电网故障…

嵌入式开发学习之STM32F407串口(USART)收发数据(三)

嵌入式开发学习之STM32F407串口(USART)收发数据(三) 开发涉及工具一、选定所使用的串口二、配置串口1.配置串口的I/O2.配置串口参数属性3.配置串口中断4.串口中断在哪里处理5.串口如何发送字符串 三、封装串口配置库文件1.创建头文…

低代码技术这么香,如何把它的开发特点发挥到极致?

前言 什么是低代码技术? 低代码是一种可视化软件开发方法,通过最少的编码更快地交付应用程序。图形用户界面和拖放功能使开发过程的各个方面自动化,消除了对传统计算机编程方法的依赖。 文章目录 前言低代码平台怎么选?用友Yonbu…

2024上海国际智慧城市展览会(世亚智博会)智慧城市,数字中国

在数字化、智能化的时代背景下,智慧城市成为了全球瞩目的焦点。而作为智慧城市领域的重要盛会,2024上海国际智慧城市展览会(简称:世亚智博会)则将再次汇聚全球目光。此次展览将于2024年3月26日至28日在上海跨国采购会展…

Java项目调用Python脚本(基于idea)

前期准备 1.首先需要在本地环境中安装配置python环境 Python(含PyCharm及配置)下载安装以及简单使用(Idea) 博主本次使用python版本为py3.7.3 2.idea安装python插件 位置:File->Settings->Plugins->python->安装后重启即可 3.引入jython依赖 &l…

在IE浏览器下fixed定位容器随着滚动条出现抖动问题(实测有效)

在ie浏览器下使用fixed定位的容器随着滚动条滚动出现晃动,这种问题比较常见,以下我们给两个解决方案。 方案一 把滑动滚动取消 方案二 在vue组件的created生命周期中添加此代码 document.addEventListener(wheel,function(event) {event.preventDefa…

Jenkins发布失败记录

Exception when publishing, exception message [Exec exit status not zero. Status [127]] 见链接:Jenkins发布时常见异常(持续更新...)_exception when publishing, exception message [exec_码农StayUp的博客-CSDN博客 The remote end hu…

华为云云耀云服务器L实例评测|企业项目最佳实践之包管理工具安装软件(六)

华为云云耀云服务器L实例评测|企业项目最佳实践系列: 华为云云耀云服务器L实例评测|企业项目最佳实践之云服务器介绍(一) 华为云云耀云服务器L实例评测|企业项目最佳实践之华为云介绍(二) 华为云云耀云服务器L实例评测&#xff5…

大模型引发“暴力计算”,巨头加速推进液冷“降温”

点击关注 文|姚悦 编|王一粟 一进入部署了液冷服务器的数据中心,不仅没有嘈杂的风扇声,甚至在不开空调的夏日也完全没有闷热感。 在大模型引发“暴力计算”的热潮下,数据中心的上下游,正在加紧推进液冷“…

Apipost一键压测已支持导入CSV文件

最近更新中Apipost对UI页面进行了一些调整,另外一键压测功能支持参数化!本篇文章将详细介绍这些改动! API调试页面的细节改动 在请求区填入请求参数或脚本时会有相应的标识 如在Query中填入多个参数时上方会展示数量 在预、后执行脚本中写…

Seata入门系列【7】Seata之TCC模式入门案例

1 前言 Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。 TCC 与 Seata AT 事务一样都是两阶段事务,它与 AT 事务的主要区别为: TCC 对业务代码侵入严重:每个阶段的数据操作都要自己进行…

最好的开放式蓝牙耳机有哪些?排名前五的开放式耳机五强

越来越多的人开始选择蓝牙耳机作为他们的音频解决方案。蓝牙耳机市场提供了各式各样的选择,不仅有常见的头戴式、耳塞式和半入耳式,还有一种备受欢迎的"开放式耳机"。今天,我将向大家介绍一些优秀的开放式蓝牙耳机款式,…

网站如何有效防止网络攻击

互联网上的网站和应用程序受到各种威胁,如黑客、恶意软件和数据泄漏。因此,了解如何解决网站被攻击的问题至关重要。本文将介绍一些简单的步骤,帮助您提高您的网站的安全性。 确认攻击 要解决网站被攻击的问题,首先需要识别是否遭…

将中文名格式化输出为英文名

要求: 编写Java程序,输入样式为:Zhong wen ming的人名,以 Ming,Zhong.W 的形式打印出来。其中.W是中间单词的首字母;例如输入”Willian Jefferson Clinton“,输出形式为:Clinton,Willian.J public static …

FPGA project : flash_write

本实验重点学习了: flash的页编程指令pp。 在写之前要先进行擦除(全擦除和页擦除); 本实验:先传写指令,然后进入写锁存周期,然后传页编程指令,3个地址; 然后传数据&a…

攻防千层饼

近年来,网络安全领域正在经历一场不断升级的攻防对抗,这场攻防已经不再局限于传统的攻击与防御模式,攻击者和防守者都已经越发熟练,对于传统攻防手法了如指掌。 在这个背景下,攻击者必须不断寻求创新的途径&#xff0…

Hadoop问题:start-all.sh显示未找到命令

在sbin文件夹下是start-all.sh可以运行的,但是到了别的文件夹下就不行了,于是想到了是文件路径问题,因为hadoop环境是和java环境一起配置的导致sbin写成了bin 解决办法: 打开.bashrc配置hadoop的环境变量 sudo vim ~/.bashrc …