前端使用JavaScript实现一个LRU缓存

引言

LRU(Least Recently Used)算法是一种广泛应用于内存管理和缓存系统的策略,在微前端、状态管理以及性能优化等场景下,合理使用缓存机制能够有效提升应用性能。本文将介绍LRU算法的基本原理,并通过JavaScript实现案例,帮助读者理解其在前端开发中的应用场景。

LRU算法原理

LRU(最近最少使用)算法是一种常用的缓存淘汰策略,它假定“最近最久未使用的数据在未来被访问的可能性最小”。当缓存空间不足时,LRU会优先移除最近最少使用的数据,为新数据腾出存储空间。

实现方式

哈希表适合快速查找、插入和删除的场景,而双向链表适合频繁插入和删除节点的场景。在某些情况下,这两种数据结构也可以结合实现LRU缓存算法时,可以使用哈希表存储 key 和对应的节点,双向链表存储实际的数据节点,以实现快速的查找和插入删除操作。

图解示例

假设设计一个容量为3的LRU缓存

  1. 首先添加3个元素
  2. 哈希表中依次添加数据的key值:key1,key2,key3
  3. 双向链表存储哈希对(key,value),key3是最后一个添加的(最新添加记录是key3),那么key3对应的哈希值添加到链表的头部node3。表示最近使用的。
    在这里插入图片描述

这个时候如果要添加第四个数据,key4放入哈希表,node4放入双向链表头部,node4包含key4,value4,然而此时容量不足,需要删除一个元素,从尾部删除一个最久没使用的元素,删除上面图示中的node1的数据,同时在哈希表中删除node1对应的key1
把node4添加到链表头部,key4添加进哈希表是同步进行的。
在这里插入图片描述

如果最近要访问key3,需要把node3从当前位置删除,并插入到链表头部
在这里插入图片描述

简而言之:

每次添加元素到链表中的时候都是从头部添加

每次删除元素的时候都是从尾部删除

删除的时候同时从哈希表里面删除对应的key

再次访问的元素,需要把元素移动到链表的头部

实现代码

使用哈希链表,可以在每个缓存项的节点上同时存储键值信息以及指向链表中前后节点的引用。当一个缓存项被访问时,先通过哈希表找到对应节点,然后将其从原有位置移出并插入链表尾部

class LRUCacheNode {constructor(key, value) {this.key = key;this.value = value;this.prev = null;this.next = null;}
}class LRUCache {constructor(capacity = 500) {this.capacity = capacity;this.cacheMap = new Map(); // 使用哈希表存储键值对this.doubleLinkedList = new DoublyLinkedList(); // 双向链表维护缓存顺序}get(key) {if (this.cacheMap.has(key)) {const node = this.cacheMap.get(key);this.doubleLinkedList.moveToTail(node); // 将节点移动到链表尾部,表示最新访问return node.value;}return -1; // 或者返回null,表示key不存在于缓存中}put(key, value) {if (this.cacheMap.has(key)) {const node = this.cacheMap.get(key);node.value = value;this.doubleLinkedList.moveToTail(node);} else {if (this.cacheMap.size >= this.capacity) {const headNode = this.doubleLinkedList.deleteHead();this.cacheMap.delete(headNode.key); // 移除最旧的缓存项}const newNode = new Node(key, value);this.cacheMap.set(key, newNode);this.doubleLinkedList.addToTail(newNode);}}
}// 双向链表类和节点类的实现略(根据实际需求实现)
class Node {constructor(key, value) {this.key = key; // 节点键值this.value = value; // 节点数据值this.prev = null; // 前驱节点引用this.next = null; // 后继节点引用}
}
class DoublyLinkedList {constructor() {this.head = null; // 头节点this.tail = null; // 尾节点}/*** 添加节点到链表尾部* @param {Node} newNode 新节点*/addToTail(newNode) {if (!this.head) {this.head = newNode;this.tail = newNode;} else {newNode.prev = this.tail;this.tail.next = newNode;this.tail = newNode;}}/*** 移除头节点并返回* @returns {Node | null} 删除的头节点或null(如果链表为空)*/deleteHead() {if (!this.head) return null;const deletedNode = this.head;this.head = this.head.next;if (this.head) {this.head.prev = null;} else {this.tail = null;}return deletedNode;}/*** 将指定节点移动到链表尾部* @param {Node} node 需要移动的节点*/moveToTail(node) {if (node === this.tail) return; // 如果已经是尾节点,则无需移动// 断开当前节点与前后节点的连接node.prev.next = node.next;if (node.next) node.next.prev = node.prev;// 将节点添加至链表尾部this.addToTail(node);}// 其他可能的方法,如查找节点、在指定位置插入节点等...
}

使用场景

路由缓存:Vue.js 中的 keep-alive 组件虽然并未直接采用LRU算法,但在实际项目中,我们可以基于LRU策略自定义实现路由组件的缓存功能。
资源加载:对于频繁请求且响应较慢的API,可以通过LRU缓存最近请求的结果,减少网络请求次数。
状态管理:在Vuex或Redux等状态管理库中,也可以利用LRU算法进行缓存,避免频繁计算或获取昂贵的状态。
业务场景:电商大促,浏览器浏览历史,微博热点。实现的具体可能不同,但是思路均可使用LRU缓存实现.

网页浏览历史

实现一个简单的浏览历史记录功能

class LRUCache {constructor(capacity) {this.capacity = capacity;this.cache = new Map();}get(key) {if (this.cache.has(key)) {const value = this.cache.get(key);// 删除旧数据再重新插入,以更新最近访问的顺序this.cache.delete(key);this.cache.set(key, value);return value;}return null;}put(key, value) {if (this.cache.has(key)) {this.cache.delete(key);} else if (this.cache.size >= this.capacity) {// 超出容量时删除最久未访问的数据(即最近使用频率最低的数据)const keys = this.cache.keys();this.cache.delete(keys.next().value);}this.cache.set(key, value);}displayHistory() {console.log("Browser History:");for (let [key, value] of this.cache) {console.log(key + " -> " + value);}}
}// 使用示例
const historyCache = new LRUCache(5); // 设置缓存容量为5historyCache.put("Page 1", "www.page1.com");
historyCache.put("Page 2", "www.page2.com");
historyCache.put("Page 3", "www.page3.com");
historyCache.get("Page 1");// 输出浏览历史记录
historyCache.displayHistory();

在上面的示例中,LRUCache 类实现了一个简单的 LRU 缓存,通过 get 方法获取历史记录,并通过 put 方法添加历史记录。displayHistory 方法用于展示浏览历史记录。

可以根据实际需求进一步扩展和优化这个示例,比如添加时间戳来记录访问时间、持久化历史记录到本地存储等功能。希望这个示例能帮助你实现浏览历史记录功能。

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

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

相关文章

C++之对象的使用

1、static成员 2、static成员优点 2、static成员函数 静态成员函数不能访问非静态成员原因:因为没有this指针。也不可以访问非静态成员函数。 可以通过对象来访问静态成员,但是不推荐这么使用,会让人误解成这个x_是属于对象的,但…

蓝桥杯练习系统(算法训练)ALGO-932 低阶行列式计算

资源限制 内存限制&#xff1a;64.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 给出一个n阶行列式(1<n<9)&#xff0c;求出它的值。 输入格式 第一行给出两个正整数n,p&#xff1b;   接下来n行&…

探索Python的包与模块:构建项目的基石

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、模块与包的基础认知 1. 模块的定义与创建 2. 包的组织与管理 二、模块与包的进阶使用…

Unity功能——设置Camera,实现玩家被攻击后晃动效果

一、方法说明&#xff1a; 来源&#xff1a;siki学院&#xff1a;Unity项目捕鱼达人&#xff0c;功能学习记录&#xff1b; 效果摘要&#xff1a;通过调整相机移动&#xff0c;视觉感觉玩家面板剧烈晃动&#xff0c;实现被boss攻击时的震动效果。 使用场景说明&#xff1a; …

开源远程协助:分享屏幕,隔空协助!

&#x1f5a5;️ 星控远程协助系统 &#x1f5b1;️ 一个使用Java GUI技术实现的远程控制软件&#xff0c;你现在就可以远程查看和控制你的伙伴的桌面&#xff0c;接受星星的指引吧&#xff01; 支持系统&#xff1a;Windows / Mac / Linux &#x1f31f; 功能导览 &#x1f…

部署Prometheus + Grafana实现监控数据指标

1.1 Prometheus安装部署 Prometheus监控服务 主机名IP地址系统配置作用Prometheus192.168.110.27/24CentOS 7.94颗CPU 8G内存 100G硬盘Prometheus服务器grafana192.168.110.28/24CentOS 7.94颗CPU 8G内存 100G硬盘grafana服务器 监控机器 主机名IP地址系统配置k8s-master-0…

栈和队列专题(LeetCode)

目录 有效的括号题解代码加解释 用队列实现栈题解代码加解释 设计循环队列题解代码加解释 用栈实现队列题解代码加解释 有效的括号 题解 左括号从s字符串中取出来放入栈中 s中就只有右括号了 那么栈顶的左括号和s的右括号匹配即可 代码中也详细解释了左括号和右括号多少的问题…

DataGear 制作服务端分页的数据可视化看板

DataGear 2.3.0 版本新增了附件图表数据集特性&#xff08;在新建图表时将关联的数据集设置为 附件 &#xff0c;具体参考官网文档定义图表章节&#xff09;&#xff0c;在制作看板时&#xff0c;可以基于此特性&#xff0c;结合dg-chart-listener&#xff0c;利用服务端数据扩…

深度学习——自己的训练集——图像分类(CNN)

图像分类 1.导入必要的库2.指定图像和标签文件夹路径3.获取文件夹内的所有图像文件名4.获取classes.txt文件中的所有标签5.初始化一个字典来存储图片名和对应的标签6.遍历每个图片名的.txt文件7.随机选择一张图片进行展示8.构建图像的完整路径9.加载图像10.检查图像是否为空 随…

nuxt3+Element Plus项目搭建过程记录

背景 本文只记录项目搭建过程中遇到的一些问题和关键点&#xff0c;nuxt框架的说明和API请参照官网学习 官网&#xff1a;https://nuxt.com/docs/getting-started/introduction 1. 初始化项目 指令如下: npx nuxilatest init <project-name>我在安装过程中出现报错&a…

SpringBoot + MybatisPlus

SpringBoot MybatisPlus 整合记录 1. 硬件软件基本信息2. 相关链接3. 通过idea快速生成一个Springboot项目4. 启动报错问题解决问题一&#xff1a;Springboot启动的时候报错提示 “没有符合条件的Bean关于Mapper类型”问题二&#xff1a;启动的时候提示需要一个Bean&#xff0…

大数据框架总结(全)

☔️ 大数据框架总结&#xff08;全&#xff09; 关注“大数据领航员”&#xff0c;在公众号号中回复关键字【大数据面试资料】&#xff0c;即可可获取2024最新大数据面试资料的pdf文件 一. Hadoop HDFS读流程和写流程 HDFS写数据流程 &#xff08;1&#xff09;客户端通过…

Aws CodeCommit代码仓储库

1 创建IAM用户 IAM创建admin用户&#xff0c;增加AWSCodeCommitFullAccess权限 2 创建存储库 CodePipeline -> CodeCommit -> 存储库 创建存储库 3 SSH 1) window环境 3.1.1 上载SSH公有秘钥 生成SSH秘钥ID 3.1.2 编辑本地 ~/.ssh 目录中名为“config”的 SSH 配置文…

java:程序包javax. servLet不存在

一.原因 1.项目Tomcat 服务器依赖未导入 2.项目的 SDK 版本选择错误 二.解决方法 方案一&#xff1a; 1.选择项目结构选项 2.导入Tomcat依赖 把tomcat里面的【jsp-api.jar】和【servlet-api.jar】这两个包导入 方案二&#xff1a; 1.选择项目结构选项 2.选择自己的jdk版本…

Git 小白入门到进阶—(基本概念和常用命令)

一.了解 Git 基本概念和常用命令的作用 (理论) 基本概念 1、工作区 包含.git文件夹的目录&#xff0c;主要用存放开发的代码2、仓库 分为本地仓库和远程仓库&#xff0c;本地仓库是自己电脑上的git仓库(.git文件夹);远程仓库是在远程服务器上的git仓库git文件夹无需我们进行操…

收银系统源码--零售连锁店铺如何选择适合自己的收银系统?

如果你现在还认为小便利店只要简单的收款&#xff0c;只有大型的连锁便利店才需要收银软件和管理软件&#xff0c;那你就错了&#xff0c;连锁品牌的便利店是必须要用到专业的收银软件&#xff0c;但是小微型的便利店更应该要用专门的软件&#xff0c; 在各行各业逐步革新互联网…

webpack5基础和开发模式配置

运行环境 nodejs16 webpack基础 webpack打包输出的文件是bundle 打包就是编译组合 webpack本身功能 仅能编译js文件 开始使用 基本配置 五大核心概念 准备webpack配置文件 1.在根目录 2.命名为webpack.config.js 开发模式介绍 处理样式资源 处理css样式资源文件…

Oracle 证书的重要性

随着信息技术的飞速发展&#xff0c;数据库管理已成为企业运营中不可或缺的一部分。Oracle作为全球领先的数据库管理系统提供商&#xff0c;其Oracle Certified Professional&#xff08;OCP&#xff09;认证已成为数据库管理员和开发人员追求的专业认证之一。本文将深入探讨Or…

八国多语言微盘微交易所系统源码 单控点控 K线完好

安装环境linux NGMySQL5.6PHP7.2&#xff08;函数全删&#xff09;pm2管理器&#xff08;node版本选择v12.20.0&#xff09; config/ database.php 修改数据库链接 设置运行目录 public 伪静态thinkphp

设计模式8——原型模式

写文章的初心主要是用来帮助自己快速的回忆这个模式该怎么用&#xff0c;主要是下面的UML图可以起到大作用&#xff0c;在你学习过一遍以后可能会遗忘&#xff0c;忘记了不要紧&#xff0c;只要看一眼UML图就能想起来了。同时也请大家多多指教。 原型模式&#xff08;Prototyp…