使用LinkedHashMap实现固定大小的LRU缓存

news2024/9/20 20:31:21

使用LinkedHashMap实现固定大小的LRU缓存

1. 什么是LRU?

LRU是"Least Recently Used"的缩写,意为"最近最少使用"。LRU缓存是一种常用的缓存淘汰算法,它的核心思想是:当缓存满时,优先淘汰最近最少使用的项目。

LRU缓存的工作原理:

  1. 新数据插入到缓存头部
  2. 每当缓存命中(即缓存数据被访问),则将数据移到缓存头部
  3. 当缓存满时,将链表尾部的数据丢弃

LRU算法的理论基础:

LRU算法基于"时间局部性原理"(Principle of Temporal Locality),该原理指出,如果一个信息项正在被访问,那么在近期它很可能还会被再次访问。这一原理在计算机科学中广泛应用,例如在操作系统的页面置换算法中。

LRU的应用场景:

  1. 数据库缓存:减少对数据库的直接访问,提高查询速度
  2. Web应用:缓存经常访问的页面或数据
  3. 硬件设计:CPU缓存的替换策略
  4. 操作系统:页面置换算法

2. LinkedHashMap与LRU缓存

LinkedHashMap的特性:

LinkedHashMap是Java集合框架中的一个类,它继承自HashMap,但在内部维护了一个双向链表,用于保持插入顺序或访问顺序。

关键特性:

  1. 可选的排序模式:插入顺序(默认)或访问顺序
  2. 预测遍历顺序:可以按照特定顺序遍历元素
  3. 性能:大部分操作的时间复杂度为O(1)

LinkedHashMap如何支持LRU:

LinkedHashMap通过以下机制支持LRU缓存的实现:

  1. 访问顺序:通过构造函数的accessOrder参数设置为true,启用访问顺序模式
  2. 自动重排序:每次访问元素时,该元素会被移到链表末尾(最近使用)
  3. removeEldestEntry方法:允许在插入新元素时,决定是否删除最老的元素

继承LinkedHashMap并重写removeEldestEntry方法:

要实现LRU缓存,我们需要:

  1. 创建一个新类,继承LinkedHashMap
  2. 在构造函数中,设置LinkedHashMap的访问顺序为true
  3. 重写removeEldestEntry方法,当map中的元素个数超过指定容量时返回true

3. 代码实现与深入分析

代码实现:

以下是一个简洁的LRU缓存实现,包含了基本功能和性能监控:

LRUCache.java
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @desc: 使用LinkedHashMap自定义LRU缓存实现
 * @author: shy
 * @date: 2024/08/26 10:03
 */
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int capacity;
    // 命中数(性能监控)
    private int hits = 0;
    // 未命中数(性能监控)
    private int misses = 0;

    public LRUCache(int capacity) {
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > capacity;
    }

    @Override
    public V get(Object key) {
        V value = super.get(key);
        if (value != null) {
            hits++;
        } else {
            misses++;
        }
        return value;
    }

    public double getHitRate() {
        int total = hits + misses;
        return total == 0 ? 0 : (double) hits / total;
    }
}
MapTest.java
public class MapTest {
    public static void main(String[] args) {
        LRUCache<Integer, String> cache = new LRUCache<>(3);
        cache.put(1, "one");
        cache.put(2, "two");
        cache.put(3, "three");
        System.out.println(cache); // 输出: {1=one, 2=two, 3=three}
        cache.get(1);
        System.out.println(cache); // 输出: {2=two, 3=three, 1=one}

        cache.put(4, "four");
        System.out.println(cache); // 输出: {3=three, 1=one, 4=four}

        // 输出缓存命中率
        System.out.println("Hit rate: " + cache.getHitRate());
    }
}
执行结果

在这里插入图片描述

代码分析:

  1. 简洁实现:通过继承LinkedHashMap,我们只需要很少的代码就能实现LRU缓存的核心功能。
  2. 容量控制:重写removeEldestEntry方法,确保缓存大小不超过指定容量。
  3. 访问顺序:在构造函数中设置accessOrder为true,确保元素按访问顺序排列。
  4. 性能监控:添加了简单的命中率计算功能,有助于评估缓存效果。
  5. 泛型支持:使用泛型实现,增加了代码的灵活性和复用性。

4. LinkedHashMap实现LRU的优势与劣势

优势:

  1. 实现简单:

    • 利用Java标准库,无需额外依赖
    • 代码量少,易于理解和维护
  2. 性能较好:

    • 大多数操作时间复杂度为O(1)
    • 内部使用哈希表,提供快速的查找性能
  3. 功能完整:

    • 自动维护访问顺序
    • 支持快速的插入和删除操作
  4. 灵活性:

    • 可以轻松扩展,添加自定义功能(如上面的命中率计算)
    • 支持泛型,可用于各种数据类型

劣势:

  1. 内存占用:

    • 比普通HashMap占用更多内存,因为需要维护双向链表
    • 对于大容量缓存,可能会成为性能瓶颈
  2. 并发性能:

    • 默认非线程安全,在多线程环境下需要额外的同步机制
    • 全局同步可能导致高并发场景下的性能问题
  3. 功能局限:

    • 不支持过期时间等高级特性
    • 缺乏分布式缓存支持
  4. 扩展性限制:

    • 继承自LinkedHashMap,可能限制了与其他类的集成
    • 在复杂系统中,可能需要更灵活的接口设计

5. 实际应用中的注意事项

  1. 缓存大小选择:

    • 需要根据实际应用场景和可用内存来确定
    • 考虑缓存命中率和系统性能的平衡
  2. 并发处理:

    • 在多线程环境中,需要注意同步问题
    • 考虑使用 Collections.synchronizedMap() 包装 LRUCache,或使用 ConcurrentHashMap 的变体
  3. 缓存预热:

    • 在系统启动时,可以预先加载常用数据到缓存中
    • 有助于提高系统初期的响应速度
  4. 缓存一致性:

    • 当底层数据发生变化时,需要及时更新或失效缓存
    • 考虑实现缓存更新策略(如写透、延迟写入等)
  5. 监控和调优:

    • 实现缓存命中率、占用空间等指标的监控
    • 根据监控数据定期调整缓存策略

6. 替代方案和进阶技巧

  1. Guava Cache:

    • Google的Guava库提供了更强大的缓存实现
    • 支持过期时间、自动加载、最大大小限制等特性
  2. Caffeine:

    • 高性能的Java缓存库,在许多方面超越了Guava Cache
    • 提供了更灵活的配置选项和更好的并发性能
  3. 多级缓存:

    • 结合内存缓存和分布式缓存(如Redis)
    • 可以平衡访问速度和数据容量
  4. 自定义驱逐策略:

    • 除LRU外,还可以实现LFU(最不经常使用)、FIFO等策略
    • 根据实际应用需求选择或组合不同的策略
  5. hutool-cache:

    • 功能丰富的缓存工具类
    • 支持设置缓存的过期时间和最大容量
    • 支持灵活地控制缓存的生命周期和大小

通过使用LinkedHashMap实现固定大小的LRU缓存的实现,展示了如何使用LinkedHashMap创建一个简单而有效的LRU缓存。这个实现保持了代码的简洁性,同时仍然提供了基本的性能监控功能。在实际应用中,可以根据具体需求进行进一步的扩展和优化。

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

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

相关文章

二、设置地图配置表

一、导入一个背景图 由于背景图比较大&#xff0c;需要缩小至0.73 二、写配置文件&#xff08;SO&#xff09; 使用List需要一个命名空间 写一个类&#xff0c;声明房间的出现数量和种类&#xff1b;将它实例化出来 三、枚举变量的多选 在枚举变量中标记命名空间&#xff…

C++:list篇

前言: 观看C的list前需要对链表有一些了解&#xff0c;如C语言的链表结构。本片仅介绍list容器中常用的接口函数概念以及使用。 list的概念&#xff1a; 简而言之&#xff0c;C的list是一个双向带哨兵位的链表容器模板 list的构造&#xff1a; 1.list():默认构造 2.li…

2024最新版Python+Pycharm安装教程,安装、环境配置、汉化全搞定,保姆级教学!

一、Python下载 为了节约时间&#xff0c;我将PythonPycharm安装包、集火码全部打包上传至CSDN官方&#xff0c;可放心下载&#xff0c;完全免费&#xff01;&#xff08;安装包均为最新版本&#xff09; 二、Python安装 1.双击运行本地文件夹下的python安装包&#xff08;以…

c++关于字符串的联系

提示并输入一个字符串&#xff0c;统计该字符串中字母个数、数字个数、空格个数、其他字符的个数 #include <iostream> #include<string> using namespace std;int main() {string s1;int letter0,digit0,space0,other0;cout<<"请输入一个字符串:"…

Visual Studio 2022 自定义字体大小

常用编程软件自定义字体大全首页 文章目录 前言具体操作1. 打开字体和颜色对话框2. 设置【文本编辑器】字体大小3. 设置【编辑器智能提示框】字体大小4. 设置【编辑器方法提示框】字体大小5. 设置【输出框】、【错误列表框】字体大小6. 设置【终端】字体大小7. 设置【程序包管理…

线程基础和线程间通信

作业一:创建3个线程&#xff0c;一个子线程拷贝文件的前一半&#xff0c;一个子线程拷贝后一半文件&#xff0c;主线程回收子线程资源。 #include <myhead.h> //定义结构体变量 typedef struct {const char *src_file;const char *dest_file;int start;int end; } Copy;…

IAR软件配置笔记

Project->Optiions->配置Device Debug中配置 C/C Compiler中配置 优化等级 C语法标准选择 回到主界面&#xff0c;Tools->Options 字体调整 Editor更改缩进数 Project->Make编译 调试模式和编辑模式的View菜单栏不一样http://t.csdnimg.cn/JsWjy

百度网盘下载的文件怎么打印?

一、自有打印机情况下的打印方法 如果您家中或办公室配备有打印机&#xff0c;那么打印百度网盘上的文件就变得非常简单了。首先&#xff0c;请确保您的打印机已连接并设置妥当。以下是基本的操作流程&#xff1a; 下载文件&#xff1a;从百度网盘中下载所需的文件至您的计算…

关于重复消费的一些解决方案

重复消费问题一直是一个热点问题&#xff0c;不管是面试还是实际工作过程中都会遇到&#xff0c;今天我就盘一下这个问题。 1. 重复消费是怎么出现的 重复消费的问题出现的情况有很多&#xff0c;我列举一下常见的吧&#xff1a; 用户重复提交表单。 用户使用软件恶意刷单。…

【Qt】基于VTK9.1+VS2019+Qt5.15.2的点云可视化程序开发

目录 0. 写在前面 1. 开发环境 2. VS 2019 PCL 1.12.1 配置 3. Qt 5.15.2安装 & Qt-VS插件安装 & VTK 9.1.0编译安装 4. 点云显示demo 4.1 UI 4.2 头文件 4.3 主程序 4.4 显示效果 5. 写在最后 0. 写在前面 由于工作需要和个人爱好&#xff0c;我最近在学习…

lenovo M7328W 复印二页不同的内容到同一张白纸上,要求要全都是正向,就是一个方向显示。当然也可以不一样,那就随便放不要求正反,只要求位置一致。

1、先上个图&#xff0c;打开软件&#xff1a; 按一下电源键&#xff0c;将休眠中的打印机&#xff0c;变成待机状态&#xff0c;这样才可以复印。 2、打开复印的盖板&#xff0c;将要复印的一面帖在最右侧&#xff0c;右侧要留一些空白距离&#xff0c;然后点击复印按钮&#…

数据存储单位——真题详解

在计算机中&#xff0c;以下哪个选项描述的数据存储容量最小?( ) A. 字节(byte) B. 比特(bit) C. 字(word) D. 千字节(kilobyte) 【答案】 B 【解析】 本题考查计算机数据存储容量单位的比较&#xff0c;这个题目看起来很简单&#xff0c;但有的老师甚至都会做错&am…

华为海思招聘-芯片与器件设计工程师-数字芯片方向- 机试题——(共九套)(每套四十题)

华为海思招聘-芯片与器件设计工程师-数字芯片方向- 机试题-题目分享——共九套&#xff08;每套四十题&#xff09; 岗位——芯片与器件设计工程师 岗位意向——数字芯片 真题题目分享&#xff0c;完整版带答案(有答案和解析&#xff0c;答案非官方&#xff0c;未仔细校正&am…

天下通商城:数字时代的智能消费助手

随着数字经济的飞速发展&#xff0c;消费方式也在不断革新。天下通网络科技&#xff08;海南&#xff09;有限公司顺应这一趋势&#xff0c;推出了天下通商城&#xff0c;这一平台不仅简化了购物流程&#xff0c;还为用户提供了丰富的智能消费工具&#xff0c;成为现代消费者的…

解决国产手机wda不能联网问题

前言 国产手机在通过tidevice无线调试启动wda&#xff08;webdriveragent&#xff09;后&#xff0c;会有报错&#xff1a; request error:(‘Connectionaborted.’, SocketError(‘socket timeout’))AirtestIDE或者appnium inspector 无法连接手机显示模拟器界面&#xff0c…

音频导出后为什么效果变差了 FL Studio音频导出设置推荐

FL Studio是一款功能强大的编曲软件&#xff0c;除了可以编曲之外&#xff0c;FL Studio还支持各种音频格式导出。有的小伙伴在使用FL Studio导出音频后&#xff0c;会发现的导出的音频效果不理想&#xff0c;这很大的原因可能是导出设置不对造成的。下面给大家详细讲解&#x…

20240827让飞凌的OK3588-C核心板在Linux R4下通过rockit工具的VI功能获取SONY索尼8530机芯的YUV图

20240827让飞凌的OK3588-C核心板在Linux R4下通过rockit工具的VI功能获取SONY索尼8530机芯的YUV图 2024/8/27 19:40 缘起&#xff0c;我司使用飞凌的OK3588-C的核心板&#xff0c;系统是Linux R4版本。 机芯选用SONY索尼的HDMI OUT接口的8530&#xff0c;输出格式有BGR3/NV16。…

CMake之PUBLIC、PRIVATE、INTERFACE

竹杖芒鞋轻胜马,谁怕?一蓑烟雨任平生~ 个人主页&#xff1a; rainInSunny | 个人专栏&#xff1a; C那些事儿、 Qt那些事儿 文章目录 写在前面抽象版解释头文件和链接库传递测试代码结构PUBLIC传递PRIVATE传递INTERFACE传递 写在前面 使用CMake必然离不开target_include_dir…

LVGL从SD卡读取图片并显示到MCU LCD屏详细笔记教程

1、前言 在上一篇LVGL专题文章中已经讲解了如何将LVGL与FatFs链接起来&#xff0c;实现LVGL对SD卡中的数据进行读写操作。本文在上一文的基础上&#xff0c;将实现LVGL读取文件系统中的图片文件&#xff08;BMP、PNG、JPG&#xff09;&#xff0c;并显示到MCU设备中的LCD屏中。…

转行软件测试必需要知道的知识

1前言 随着现在互联网行业的迅猛发展&#xff0c;越来越多的人想涌入进来&#xff0c;因为大家都觉得IT行业工资高&#xff0c;做测试门槛又低&#xff0c;是的&#xff0c;入门门槛确实比较低&#xff0c;但是要做好的话&#xff0c;还是有点难的&#xff0c;毕竟术业有专攻嘛…