LRU算法的Java实现

2023-11-15

一、LRU算法介绍

LRU算法全称Least Recently Used,也就是检查最近最少使用的数据的算法。这个算法通常使用在内存淘汰策略中,用于将不常用的数据转移出内存,将空间腾给最近更常用的“热点数据”。

算法很简单,只需要将所有数据按使用时间排序,在需要筛选出LRU数据时,取排名靠后的即可。

二、Java中的LRU实现思路

根据LRU算法,在Java中实现需要这些条件:

  • 底层数据使用双向链表,方便在链表的任意位置进行删除,在链表尾进行添加,这一点用单链表比较费劲,当然用数组等结构也都很费劲,当然双向链表在查找时也麻烦,但下述可以结合HashMap使用。
  • 需要将链表按照访问(使用)顺序排序。
  • 数据量超过一定阈值后,需要删除Least Recently Used数据。

三、Java中一个简单的LRUCache实现

对于上述的实现思路,java.util.LinkedHashMap已经实现了其中的99%,因此直接基于LinkedHashMap实现LRUCache非常简单。

LinkedHashMap为LRUCache铺垫了什么

  • 构造方法提供了accessOrder选项,开启后会get方法会有额外操作保证链表顺序按访问顺序逆序排列;

  • 底层结构使用双向链表,查找可以使用HashMap的特点;

  • 覆盖了父类HashMap的newNode方法和newTreeNode方法,这两个方法在HashMap中只是创建Node用的,而在LinkedHashMap中不但创建Node,还将Node放在链表末尾。

  • 父类HashMap提供了3个void的Hook方法,方法没做任何事:

  • afterNodeRemoval 父类在remove一个集合中存在的元素后调用;
  • afterNodeInsertion 父类在put、compute、merge后调用;
  • afterNodeAccess 父类在replace、compute、merge等替换值后会调用,LinkedHashMap在get中开启accessOrder时调用,究其根本是在对数据有操作时会调用;
  • LinkedHashMap本质上还是复用HashMap的绝大部分功能,包括底层的Node<K, V>[],因此能支持原本HashMap的功能。

  • 但是LinkedHashMap实现了父类HashMap的3个Hook方法:

  • afterNodeRemoval 实现链表的删除操作;
  • afterNodeInsertion 并没有实现链表的插入操作,但新添加了一个Hook方法boolean removeEldestEntry,当这个Hook方法返回true时,删除链表头的节点;
  • afterNodeAccess 如前所述,开启accessOrder后会将被操作的节点放在链表末尾,保证链表顺序按访问顺序逆序排列;

上一条3个方法是用来构建双向链表的,LinkedHashMap还覆盖了父类的3个方法:

  • newNode 在创建一个Node的同时,将Node添加到链表末尾;
  • newTreeNode 创建TreeNode的同时,将Node添加到链表末尾;
  • get 完成get功能的同时,如果accessOrder开启,会调afterNodeAccess将Node移动到链表末尾,覆盖newNode和newTreeNode方法后,在put方法中调用的newNode和newTreeNode方法也就连带实现了链表的插入操作

综上,我们可以了解到LinkedHashMap为什么能够轻松实现LRUCache。

  • 继承父类HashMap,拥有HashMap的功能,因此在查找一个节点时时间复杂度为O(1),再加上链表是双向,做链表任意节点的删除工作就非常简单。
  • 通过HashMap提供的3个Hook方法并覆盖了2个创建Node的方法,实现了自身链表的添加、删除工作,保证在不影响原本Array功能的前提下,正确完成自身的链表构建;这个过程实际上均是通过Hook方式增强原有功能的,因为原本的HashMap中创建节点其实也是使用的Hook方法。
  • 提供属性accessOrder并实现了afterNodeAccess方法,因此能够根据访问或操作顺序将最近使用或最近插入的数据放在链表尾,越久没被使用的数据就越靠近链表头,实现了整个链表按照LRU的要求排序。
  • 提供了一个Hook方法boolean removeEldestEntry,这个方法返回true时将会删除表头节点,即LRU中应当淘汰的节点,但是这个方法在LinkedHashMap中的实现永远返回false。

到这为止,实现一个LRUCache就很简单了:实现这个removeEldestEntryHook方法,给LinkedHashMap设置一个阈值,那么超过这个阈值时就会进行LRU淘汰。

示例:

// 继承LinkedHashMap
    public class LRUCache<K, V> extends LinkedHashMap<K, V> {
        private final int MAX_CACHE_SIZE;

        public LRUCache(int cacheSize) {
            // 使用构造方法 public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
            // initialCapacity、loadFactor都不重要
            // accessOrder要设置为true,按访问排序
            super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true);
            MAX_CACHE_SIZE = cacheSize;
        }

        @Override
        protected boolean removeEldestEntry(Map.Entry eldest) {
            // 超过阈值时返回true,进行LRU淘汰
            return size() > MAX_CACHE_SIZE;
        }

    }

注:LinkedHashMap是线程不安全的,可以参考Dubbo中com.alibaba.dubbo.common.utils.LRUCache的实现。

四、LRU缓存命中率问题

当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。

分析:
LRU只适合缓存热点数据,偶发性、周期性的批量操作会将LRU的热点缓存污染,于是像LRU-K、URL-Two queues原理、Multi Queue原理等都是将热点数据识别出来再放入LRU缓存队列,但是热点数据识别都是有一个识别窗口的,比如最近10000次内操作过两次的认为是热点数据会放入LRU缓存,避免LRU缓存的热点数据被污染掉,降低缓存命中率。

具体的解决方案大家可以参考:https://www.jianshu.com/p/d533d8a66795

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LRU算法的Java实现 的相关文章

  • [每日两题系列]刷算法题咯~~

    今日题目 卡片 直线 本系列所选题目均来自力扣或者牛客网站 所选题目主要是以其中的简单题为主 中等题为辅 包含少数困难题 原因是 本人目前能力还不够 开展这个系列的目的是督促自己 在暑假的时间里也要保持有一定的刷题量 拒绝摆烂 话不多说 直

随机推荐

  • python矩阵教程_numpy教程:矩阵matrix及其运算

    numpy矩阵简介 NumPy函数库中存在两种不同的数据类型 矩阵matrix和数组array 都可以用于处理行列表示的数字元素 虽然它们看起来很相似 但是在这两个数据类型上执行相同的数学运算可能得到不同的结果 其中NumPy函数库中的ma
  • 插入MySQL数据库前去除重复数据的几种方法

    在数据存储过程中 可能会遇到数据主键重复的情况 我们可以通过下面几个方法进行处理 1 若数据不存在插入 存在更新 2 使用duplicate key关键字 如插入数据时发生主键冲突就更新数据 3 使用Ingore关键字 4 使用replac
  • BoxFit(缩放模式、自适应模式)

    类似于Android原生的ImageView ScaleType 以下是Flutter提供的Box缩放类型 fill Box被完全填充 相当于ScaleType的FIT XY contain 保持Box的纵横比至至少有一边填充满父控件 相当
  • 单例模式 -- 懒汉模式&饿汉模式

    目录 一 单例模式是什么 二 饿汉模式 三 懒汉模式 一 单例模式是什么 单例模式是一种设计模式 用于将类的实例化限制为一个对象 它确保一个类只有一个实例 并提供了该实例的全局访问点 这种模式被广泛用于创建对象的唯一实例 例如数据库连接和日
  • LCD(五)Backlight背光子系统

    一 Backlight背光子系统概述 LCD的背光原理主要是由核心板的一根引脚控制背光电源 一根PWM引脚控制背光亮度组成 应用程序可以通过改变PWM的频率达到改变背光亮度的目的 Backlight背光子系统构建过程结构关系图 黑色加粗部分
  • ONNX 运行时报错 ORT_RUNTIME_EXCEPTION Ort::Exception 未经处理的异常

    1 运行报错 前段时候推理时遇到一个非常奇怪的bug ONNX模型在运行时会报ORT RUNTIME EXCEPTION的异常 2 错误排查 继续运行 断点看到是在Session Run 的时候报错 断点逐语句跟踪没有更多详情的信息 重新看
  • jsp 购物车

  • 墨者学院——SQL注入漏洞测试(时间盲注)

    点击链接进入题目 点进网页 在url后加 type 1 发现没有回显 上传 type 1 and sleep 10 发现网页有明显延迟 说明sleep函数被执行 该网页存在时间注入 通过构造payload去获得数据库长度 x为猜想的数据库长
  • 【LSTM预测】基于双向长短时记忆BiLSTM(多输入单输出)数据预测含Matlab源码

    1 简介 针对长短期记忆循环神经网络在对时间序列进行学习时存在早期特征记忆效果差 难以充分挖掘整个网络流量特征等问题 提出一种基于双向长短期记忆循环神经网络的网络流量预测方法 以提高网络流量预测的准确性 对网络流量序列进行双向学习 避免单向
  • Android的手势识别

    首先 在Android系统中 每一次手势交互都会依照以下顺序执行 接触接触屏一刹那 触发一个MotionEvent事件 该事件被OnTouchListener监听 在其onTouch 方法里获得该MotionEvent对象 通过Gestur
  • Variable used in lambda expression should be final or effectively final

    问题描述 在使用java8lambda表达式的时候 有时候会遇到这样的编译报错 这句话的意思是 lambda表达式中使用的变量应该是final或者是有效的final 在Java8之前 匿名类中如果要访问局部变量的话 那个局部变量必须显式的声
  • LeetCode-1488. Avoid Flood in The City

    Your country has an infinite number of lakes Initially all the lakes are empty but when it rains over the nth lake the n
  • 迅雷5引发的Dos Generic SYNFlood网络攻击

    迅雷5引发的Dos Generic SYNFlood网络攻击 使用卡巴斯基的各位 有没有注意到卡巴最近经常会报Dos Generic SYNFlood 网络攻击 而且一报起来就没完没了 网上有人居然收到几千条还没崩溃 真是有定力 今天突然发
  • flutter_html出现蓝底

    Html data div style background color FFFFFF 123 div 原因是不支持大写颜色 替换为小写即可 String upperColor2Lower String text RegExp reg ne
  • 激发云力量--打造我的云端工具集

    0 前言 日常工作中 有很多小需求 作为码农 总喜欢自己动手做点小东西出来 也成为学习与实践的好机会 在使用腾讯云过程中 从环境搭建 各个小需求的构思 前后端技术的琢磨 学习 使用 收获很大 现在整理出来和大家分享 先说说做了哪些事情 都来
  • 深度学习在训练和测试阶段使用显卡的情况是否必须完全一致?

    问题 深度学习模型在进行训练时采用多张显卡进行训练 测试时是不是就与显卡无关了 也就是说可以利用CPU做推理 也可以使用GPU做推理 回答 是的 在深度学习模型训练时采用多张显卡进行训练 测试时模型的预测过程与显卡无关 这意味着在测试过程中
  • netware php_服务器_如何在 Netware 服务器中安装多块网卡,如果网络在扩大时服务器只装 - phpStudy...

    如何在 Netware 服务器中安装多块网卡 如果网络在扩大时服务器只装一块网卡 所有工作站采用总线结构上网 那么访问速度会很慢 另外 如果上网时某台工作站出了故障 所有的工作站都受其影响 不能工作 我们可以在服务器中安装多块网卡来解决问题
  • 【虚拟机连接Xshell详细过程】

    虚拟机连接Xshell详细过程 1 Xshell简介 2 Xftp简介 3 安装下载Xhell和Xftp 下载Xshell和上面的步骤一样 4 使用VMware连接Xhell 5 Xftp的使用 1 Xshell简介 Xshell是Wind
  • CCS软件

    目录 一 如何跳转到函数的定义 二 declaration is incompatible with 常见错误原因 三 symbol cell values redefined first defined in HARDWARE bq pa
  • LRU算法的Java实现

    一 LRU算法介绍 LRU算法全称Least Recently Used 也就是检查最近最少使用的数据的算法 这个算法通常使用在内存淘汰策略中 用于将不常用的数据转移出内存 将空间腾给最近更常用的 热点数据 算法很简单 只需要将所有数据按使