LRU 是 Least Recently Used 的缩写,即最近最少使用。作为一种经典的缓存策略,它的基本思想是长期不被使用的数据,在未来被用到的几率也不大,所以当新的数据进来时我们可以优先把这些数据替换掉。

一、基本要求

  1. 固定大小:限制内存使用。
  2. 快速访问:缓存插入和查找操作应该很快,最好是 O(1) 时间。
  3. 在达到内存限制的情况下替换条目:缓存应该具有有效的算法来在内存已满时驱逐条目。

二、数据结构

下面提供两种实现方式,并完成相关代码。

2.1 Map

在 Javascript 中,Map 的 key 是有序的,当迭代的时候,他们以插入的顺序返回键值。结合这个特性,我们也通过 Map 实现 LRU 算法。

2.2 Doubly Linked List

我们也可通过双向链表(Doubly Linked List)维护缓存条目,通过对链表的增、删、改实现数据管理。为确保能够从链表中快速读取某个节点的数据,我们可以通过 Map 来存储对链表中节点的引用。

三、Map 实现

在 初始化时 完成两件事情:

  1. 配置存储限制,当大于此限制,缓存条目将按照最近读取情况被驱逐。
  2. 创建一个用于存储缓存数据的 Map 。

在 添加数据 时:

  1. 判断当前存储数据中是否包含新进数据,如果存在,则删除当前数据
  2. 判断当前存储空间是否被用尽,如果已用尽则删除 Map 头部的数据。

    map.delete(map.keys().next().value)
  3. 插入新数据到 Map 的尾部

基于 Javascript Map 实现 LRU,代码如下:

class LRUCache {
size = 5
constructor(size) {
this.cache = new Map()
this.size = size || this.size
} get(key) {
if (this.cache.has(key)) {
// 存在即更新
let temp = this.cache.get(key)
this.cache.delete(key)
this.cache.set(key, temp)
return temp
}
return null
} set(key, value) { if (this.cache.has(key)) {
this.cache.delete(key)
} if (this.cache.size >= this.size) {
this.cache.delete(this.cache.keys().next().value)
} this.cache.set(key, value)
}
}

四、双向链表实现

4.1 定义节点类

包含 prev,next,data 三个属性,分别用以存储指向前后节点的引用,以及当前节点要存储的数据。

{
prev: Node
next: Node
data: { key: string, data: any}
}

4.2 定义链表类

包含 head、tail 属性,分别指向链表的 头节点 和 尾节点。

当从链表中读取数据时,需要将当前读取的数据移动到链表头部;添加数据时,将新节点插入到头部;当链表节点数量大于限定的阀值,需要从链表尾部删除节点。

{
head: Node
next: Node
moveNodeToHead(node)
insertNodeToHead(node)
deleteLastNode()
}

4.3 定义 LRU 类

为 LRU 定义属性:linkLine 用以存储指向链表的引用;size 用以配置存储空间大小限制;

为简化从链表中查找节点,再定义 map 属性,用以存储不同键指向链表节点的引用。

定义成员方法,set(key,value) 用以添加数据,get(key) 读取一条数据。

4.4 set(key,value)

  1. 如果 map 中存在当前 key,则修改当前节点的值,然后从链表中把当前节点移动到链表头部;
  2. 否则:
    1. 判断当前链表节点数量是否达到了存储上线,如果是,则删除链表尾部的节点。同时从 map 中移除相应的节点引用;
    2. 创建新节点,然后插入到链表头部,并添加 map 引用。

4.5 get(key)

如果 map 中存在当前 key,从链表中读取节点,将其移动到链表头部,并返回结果,否则返回空。

{
linkLine: LinkLine
map: Map
size: Number
set(key, value)
get(key)
}

4.6 代码实现

class LinkNode {
prev = null
next = null
constructor(key, value) {
this.data = { key, value }
}
} class LinkLine { head = null
tail = null constructor() {
const headNode = new LinkNode('head', 'head')
const tailNode = new LinkNode('tail', 'tail') headNode.next = tailNode
tailNode.prev = headNode this.head = headNode
this.tail = tailNode
} moveNodeToFirst(node) {
node.prev.next = node.next
node.next.prev = node.prev
this.insertNodeToFirst(node)
} insertNodeToFirst(node) {
const second = this.head.next
this.head.next = node
node.prev = this.head
node.next = second
second.prev = node
} delete(node) {
node.prev.next = node.next
node.next.prev = node.prev
} deleteLastNode() {
const last = this.tail.prev
this.tail.prev = last.prev
last.prev.next = this.tail
return last
}
} class LRUCache {
linkLine = null
map = {}
size = 5 constructor(size) {
this.size = size || this.size
this.linkLine = new LinkLine
} get(key) {
let value
if (this.map[key]) {
const node = this.map[key]
value = node.value
this.linkLine.moveNodeToFirst(node)
}
return value
} set(key, value) {
if (this.map[key]) {
const node = this.map[key]
node.value = value
this.linkLine.moveNodeToFirst(node)
} else {
// 删除最后一个元素
if (Object.keys(this.map).length >= this.size) {
const lastNode = this.linkLine.deleteLastNode()
delete this.map[lastNode.data.key]
} const newNode = new LinkNode(key, value)
this.linkLine.insertNodeToFirst(newNode)
this.map[key] = newNode
}
}
}

https://gauliang.github.io/blogs/2022/lru-algorithm/

Javascript 手写 LRU 算法的更多相关文章

  1. 手写LRU算法

    import java.util.LinkedHashMap; import java.util.Map; public class LRUCache<K, V> extends Link ...

  2. [纯C#实现]基于BP神经网络的中文手写识别算法

    效果展示 这不是OCR,有些人可能会觉得这东西会和OCR一样,直接进行整个字的识别就行,然而并不是. OCR是2维像素矩阵的像素数据.而手写识别不一样,手写可以把用户写字的笔画时间顺序,抽象成一个维度 ...

  3. 22 道高频 JavaScript 手写面试题及答案

    实现防抖函数(debounce) 防抖函数原理:在事件被触发n秒后再执行回调,如果在这n秒内又被触发,则重新计时. 那么与节流函数的区别直接看这个动画实现即可. 手写简化版: // 防抖函数 cons ...

  4. 08.手写KNN算法测试

    导入库 import numpy as np from sklearn import datasets import matplotlib.pyplot as plt 导入数据 iris = data ...

  5. JavaScript手写new方法

    1.看一下正常使用的new方法 function father(name){ this.name=name; this.sayname=function(){ console.log(this.nam ...

  6. HashMap+双向链表手写LRU缓存算法/页面置换算法

    import java.util.Hashtable; class DLinkedList { String key; //键 int value; //值 DLinkedList pre; //双向 ...

  7. javaScript 手写图片轮播

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  8. 用C实现单隐层神经网络的训练和预测(手写BP算法)

    实验要求:•实现10以内的非负双精度浮点数加法,例如输入4.99和5.70,能够预测输出为10.69•使用Gprof测试代码热度 代码框架•随机初始化1000对数值在0~10之间的浮点数,保存在二维数 ...

  9. 手写LRU实现

    完整基于 Java 的代码参考如下 class DLinkedNode { String key; int value; DLinkedNode pre; DLinkedNode post; } LR ...

随机推荐

  1. 1_day01_java入门

    java入门 学习目标: 1.熟悉计算机编程语言 2.熟练掌握java特点 3.熟练配置java开发环境 4.熟练编写入门程序 5.熟练编写注释信息 一.计算机语言 1.1 什么是编程语言 计算机语言 ...

  2. 在 IDEA 里下个五子棋不过分吧?

    大家好,我是二哥呀!今天给大家分享一个基于Netty的IDEA即时聊天插件,可以实现即时聊天.游戏对战(下棋). GitHub 地址:https://github.com/anlingyi/xecha ...

  3. Netty源码解读(二)-服务端源码讲解

    简单Echo案例 注释版代码地址:netty 代码是netty的源码,我添加了自己理解的中文注释. 了解了Netty的线程模型和组件之后,我们先看看如何写一个简单的Echo案例,后续的源码讲解都基于此 ...

  4. Python3.7+jieba(结巴分词)配合Wordcloud2.js来构造网站标签云(关键词集合)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_138 其实很早以前就想搞一套完备的标签云架构了,迫于没有时间(其实就是懒),一直就没有弄出来完整的代码,说到底标签对于网站来说还是 ...

  5. 金玉良缘易配而木石前盟难得|M1 Mac os(Apple Silicon)天生一对Python3开发环境搭建(集成深度学习框架Tensorflow/Pytorch)

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_189 笔者投入M1的怀抱已经有一段时间了,俗话说得好,但闻新人笑,不见旧人哭,Intel mac早已被束之高阁,而M1 mac已经 ...

  6. 使用 Java 操作 Redis

    Jedis 1. 概述 Jedis 是一款使用 Java 操作 Redis 的工具,有点类似于 JDBC 2. 引入依赖 <dependency> <groupId>redis ...

  7. 关于linux的一点好奇心(四):tail -f文件跟踪实现

    关于文件跟踪,我们有很多的实际场景,比如查看某个系统日志的输出,当有变化时立即体现,以便进行问题排查:比如查看文件结尾的内容是啥,总之是刚需了. 1. 自己实现的文件跟踪 我们平时做功能开发时,也会遇 ...

  8. Taurus.MVC 微服务框架 入门开发教程:项目部署:2、让Kestrel支持绑定多个域名转发,替代Ngnix使用。

    系列目录: 本系列分为项目集成.项目部署.架构演进三个方向,后续会根据情况调整文章目录. 本系列第一篇:Taurus.MVC V3.0.3 微服务开源框架发布:让.NET 架构在大并发的演进过程更简单 ...

  9. Redis的两种持久化机制

    Redis的两种持久化机制 1.持久化机制 client--->redis(内存)--->内存数据-数据持久化--->磁盘 两种方法 快照(Snapshot) AOF(Append ...

  10. Spring 14: Spring + MyBatis初步整合开发

    SM整合步骤 预期项目结构 新建数据库和数据表 springuser.sql脚本如下 create database ssm; use ssm; create table users( userid ...