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

使用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;}@Overrideprotected boolean removeEldestEntry(Map.Entry<K, V> eldest) {return size() > capacity;}@Overridepublic 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.mzph.cn/diannao/52860.shtml

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

相关文章

Mac下的压缩包和Win看到的不一样怎么办 Mac压缩后Win电脑看文件名会乱码

在当今多平台的数字工作环境中&#xff0c;Mac和Windows用户常常需要交换文件&#xff0c;但有时候会遇到一些兼容性问题。特别是在处理压缩文件时&#xff0c;Mac用户创建的压缩包在Windows系统中打开时&#xff0c;常常会遇到文件名乱码的问题。本文将详细讨论“Mac下的压缩包…

C语言基础(二十八)

1、冒泡排序&#xff1a; #include "date.h" #include <stdio.h> #include <stdlib.h> #include <time.h> // 函数声明 void bubbleSort(int *arr, int n); int* createRandomArray(int n, int *size); int main() { int time getTi…

22行为型设计模式——解释器模式

一、解释器模式 解释器模式&#xff08;Interpreter Pattern&#xff09;是一种行为型设计模式&#xff0c;主要用于解析和解释特定的语言或表达式。它的核心思想是为语言中的每种语法规则定义一个解释器&#xff0c;通过这些解释器将语言的表示形式转换为可执行的操作。解释器…

【Python机器学习】NLP词频背后的含义——距离和相似度

我们可以使用相似度评分&#xff08;和距离&#xff09;&#xff0c;根据两篇文档的表示向量间的相似度&#xff08;或距离&#xff09;来判断文档间有多相似。 我们可以使用相似度评分&#xff08;和举例&#xff09;来查看LSA主题模型与高维TF-IDF模型之间的一致性。在去掉了…

STM32基于HAL库串口printf使用和接收

我们这里使用HAL库直接用cubemx生成代码配置串口 1.打开cubemx&#xff0c;选择MCU型号 2.我这里使用的是STM32F103C8T6&#xff0c;根据自己的型号选择&#xff0c;这里不限制型号 3.选择时钟源 4.系统设置 5时钟配置 5.选择和配置串口 5.配置中断和中断优先级 6.工程设置…

node环境安装、vue-cli搭建过程、element-UI搭建使用过程

vue-cli 官方提供的一个脚手架&#xff0c;用于快速生成一个 vue 的项目模板&#xff1b;预先定义好的目录结构及基础代码&#xff0c;就好比咱们在创建 Maven 项目时可以选择创建一个骨架项目&#xff0c;这个骨架项目就是脚手架&#xff0c;我们的开发更加的快速 前端项目架…

探索Python测试的奥秘:nose库的魔法之旅

文章目录 探索Python测试的奥秘&#xff1a;nose库的魔法之旅1. 背景&#xff1a;为什么要用nose&#xff1f;2. nose是什么&#xff1f;3. 如何安装nose&#xff1f;4. 五个简单的库函数使用方法4.1 nose.tools.assert_true4.2 nose.tools.assert_equal4.3 nose.tools.raises4…

html2canvas、pdf-lib、file-saver将html页面导出成pdf

html2canvas、pdf-lib、file-saver将html页面导出成pdf 项目背景 需要根据用户的账号信息&#xff0c;生成一个pdf报告发给客户&#xff0c;要求报告包含echart饼图、走势图等。 方案 使用html2canvas&#xff0c;将页面转成图片&#xff0c;再通过pdf-lib将图片转成pdf文件…

Visual Studio Code离线汉化

从官网下载Visual Studio Code安装包后&#xff0c; 下载Visual Studio Code&#xff1a;https://code.visualstudio.com/ 若因网络等问题无法在线安装语言包&#xff0c;可以尝试离线安装&#xff1a; 从官网下载语言包&#xff1a; Extensions for Visual Studio family …

Stable Diffusion majicMIX_realistic模型的介绍及使用

一、简介 majicMIX_realistic模型是一种能够渲染出具有神秘或幻想色彩的真实场景的AI模型。这个模型的特点是在现实场景的基础上&#xff0c;通过加入一些魔法与奇幻元素来营造出极具画面效果和吸引力的图像。传统意义的现实场景虽然真实&#xff0c;但通常情况下缺乏奇幻性&a…

【网络世界】网络层

目录 &#x1f308;前言&#x1f308; &#x1f4c1; 网络层 &#x1f4c1; IPV4 &#x1f4c2; 什么是IP地址 &#x1f4c2; 网段划分 &#x1f4c2; 特殊IP &#x1f4c2; 内网和公网 &#x1f4c2; IPV4的危机 &#x1f4c1; IP协议格式 &#x1f4c1; 路由 &#x1f…

极限的性质【上】《用Manim可视化》

通过前面的极限的定义&#xff0c;现在是计算极限的时候了。然而&#xff0c;在此之前&#xff0c;我们需要一些极限的性质&#xff0c;这将使我们的工作变得简单一些。我们先来看看这些。 极限的性质&#xff1a; 1.常数对极限的影响 1.首先&#xff0c;我们假设和存在&…

通过Origin提取图片数据

第一步&#xff1a; Tool --> Digitizer 第二步&#xff1a;点击文件&#xff0c;导入图片 第三步&#xff1a;设置坐标轴位置和数值&#xff08;Edit Aix&#xff09; 滑动鼠标放大图片&#xff0c;将X1移动到0&#xff0c;X2移动到80&#xff0c;Y1移动到97.0&#xff0c…

Kubernetes 上安装 Jenkins

安装 Helm curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash添加 Jenkins Helm 仓库 首先添加 Jenkins Helm 仓库 helm repo add jenkins https://charts.jenkins.io helm repo update安装 Jenkins 使用 Helm 安装 Jenkins 的最新版本&…

本地部署Xinference实现智能体推理工作流(二)

第二篇章 Dify接入 Xinference 部署的本地模型 1. 安装Dify 克隆 Dify 源代码至本地。 git clone https://github.com/langgenius/dify.git 2. 启动Dify 进入 Dify 源代码的 docker 目录&#xff0c;执行一键启动命令&#xff1a; cd dify/docker cp .env.example .env d…

【OWOD论文】开放世界中OD代码_2_模型部分

简介 本文记录OWOD代码中的模型代码部分。数据部分可看我上一个博客【【OWOD论文】开放世界中OD代码_1_数据部分-CSDN博客】 模型代码 1 起步 在代码中找到 detectron2\engine\defaults.py DefaultTrainer类 __init__方法 根据上述 build_model 回溯到 detectron2\modeling\…

无人机校企合作:组装、维修、研发全面提升学生技能方好就业

无人机校企合作在组装、维修、研发等方面全面提升学生技能&#xff0c;进而促进学生就业&#xff0c;是一个具有前瞻性和实践性的教育模式。以下是对该合作模式的详细分析&#xff1a; 一、合作背景与意义 随着无人机技术的快速发展和广泛应用&#xff0c;市场对无人机专业人…

叉车(工业车辆)安全管理系统,云端监管人车信息运营情况方案

近年来&#xff0c;国家和各地政府相继出台了多项政策法规&#xff0c;从政策层面推行叉车智慧监管&#xff0c;加大叉车安全监管力度。同时鼓励各地结合实际&#xff0c;积极探索智慧叉车建设&#xff0c;实现作业人员资格认证、车辆状态认证、安全操作提醒、行驶轨迹监控等&a…

react学习之useState和useEffect

useState useState 可以使函数组件像类组件一样拥有 state&#xff0c;函数组件通过 useState 可以让组件重新渲染&#xff0c;更新视图。 实际使用 setstate()中回调函数的返回值将会成为新的state值回调函数执行时&#xff0c; React会将最新的state值作为参数传递 const A…

【HarmonyOS 4.0】@BuilderParam 装饰器

1. BuilderParam 装饰器 BuilderParam 装饰器用于装饰自定义组件(struct)中的属性&#xff0c;其装饰的属性可作为一个UI结构的占位符&#xff0c;待创建该组件时&#xff0c;可通过参数为其传入具体的内容。参数必须满足俩个条件&#xff1a; 2.1 参数类型必须是个函数&#x…