JS 实现缓存算法的示例(FIFO/LRU)


Posted in Javascript onMarch 20, 2018

FIFO

最简单的一种缓存算法,设置缓存上限,当达到了缓存上限的时候,按照先进先出的策略进行淘汰,再增加进新的 k-v 。

使用了一个对象作为缓存,一个数组配合着记录添加进对象时的顺序,判断是否到达上限,若到达上限取数组中的第一个元素key,对应删除对象中的键值。

/**
 * FIFO队列算法实现缓存
 * 需要一个对象和一个数组作为辅助
 * 数组记录进入顺序
 */
class FifoCache{
  constructor(limit){
    this.limit = limit || 10
    this.map = {}
    this.keys = []
  }
  set(key,value){
    let map = this.map
    let keys = this.keys
    if (!Object.prototype.hasOwnProperty.call(map,key)) {
      if (keys.length === this.limit) {
        delete map[keys.shift()]//先进先出,删除队列第一个元素
      }
      keys.push(key)
    }
    map[key] = value//无论存在与否都对map中的key赋值
  }
  get(key){
    return this.map[key]
  }
}

module.exports = FifoCache

LRU

LRU(Least recently used,最近最少使用)算法。该算法的观点是,最近被访问的数据那么它将来访问的概率就大,缓存满的时候,优先淘汰最无人问津者。

算法实现思路:基于一个双链表的数据结构,在没有满员的情况下,新来的 k-v 放在链表的头部,以后每次获取缓存中的 k-v 时就将该k-v移到最前面,缓存满的时候优先淘汰末尾的。

双向链表的特点,具有头尾指针,每个节点都有 prev(前驱) 和 next(后继) 指针分别指向他的前一个和后一个节点。

关键点:在双链表的插入过程中要注意顺序问题,一定是在保持链表不断的情况下先处理指针,最后才将原头指针指向新插入的元素,在代码的实现中请注意看我在注释中说明的顺序注意点!

class LruCache {
  constructor(limit) {
    this.limit = limit || 10
    //head 指针指向表头元素,即为最常用的元素
    this.head = this.tail = undefined
    this.map = {}
    this.size = 0
  }
  get(key, IfreturnNode) {
    let node = this.map[key]
    // 如果查找不到含有`key`这个属性的缓存对象
    if (node === undefined) return
    // 如果查找到的缓存对象已经是 tail (最近使用过的)
    if (node === this.head) { //判断该节点是不是是第一个节点
      // 是的话,皆大欢喜,不用移动元素,直接返回
      return returnnode ?
        node :
        node.value
    }
    // 不是头结点,铁定要移动元素了
    if (node.prev) { //首先要判断该节点是不是有前驱
      if (node === this.tail) { //有前驱,若是尾节点的话多一步,让尾指针指向当前节点的前驱
        this.tail = node.prev
      }
      //把当前节点的后继交接给当前节点的前驱去指向。
      node.prev.next = node.next
    }
    if (node.next) { //判断该节点是不是有后继
      //有后继的话直接让后继的前驱指向当前节点的前驱
      node.next.prev = node.prev
      //整个一个过程就是把当前节点拿出来,并且保证链表不断,下面开始移动当前节点了
    }
    node.prev = undefined //移动到最前面,所以没了前驱
    node.next = this.head //注意!!! 这里要先把之前的排头给接到手!!!!让当前节点的后继指向原排头
    if (this.head) {
      this.head.prev = node //让之前的排头的前驱指向现在的节点
    }
    this.head = node //完成了交接,才能执行此步!不然就找不到之前的排头啦!
    return IfreturnNode ?
      node :
      node.value
  }
  set(key, value) {
    // 之前的算法可以直接存k-v但是现在要把简单的 k-v 封装成一个满足双链表的节点
    //1.查看是否已经有了该节点
    let node = this.get(key, true)
    if (!node) {
      if (this.size === this.limit) { //判断缓存是否达到上限
        //达到了,要删最后一个节点了。
        if (this.tail) {
          this.tail = this.tail.prev
          this.tail.prev.next = undefined
          //平滑断链之后,销毁当前节点
          this.tail.prev = this.tail.next = undefined
          this.map[this.tail.key] = undefined
          //当前缓存内存释放一个槽位
          this.size--
        }
        node = {
          key: key
        }
        this.map[key] = node
        if(this.head){//判断缓存里面是不是有节点
          this.head.prev = node
          node.next = this.head
        }else{
          //缓存里没有值,皆大欢喜,直接让head指向新节点就行了
          this.head = node
          this.tail = node
        }
        this.size++//减少一个缓存槽位
      }
    }
    //节点存不存在都要给他重新赋值啊
    node.value = value
  }
}

module.exports = LruCache

具体的思路就是如果所要get的节点不是头结点(即已经是最近使用的节点了,不需要移动节点位置)要先进行平滑的断链操作,处理好指针指向的关系,拿出需要移动到最前面的节点,进行链表的插入操作。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
jquery仿京东导航/仿淘宝商城左侧分类导航下拉菜单效果
Apr 24 Javascript
返回页面顶部top按钮通过锚点实现(自写)
Aug 30 Javascript
通过正则表达式实现表单验证是否为中文
Feb 18 Javascript
jquery.cookie.js用法实例详解
Dec 25 Javascript
JS简单实现DIV相对于浏览器固定位置不变的方法
Jun 17 Javascript
AngularJs基本特性解析(一)
Jul 21 Javascript
JS实现发送短信验证后按钮倒计时功能(防止刷新倒计时失效)
Jul 07 Javascript
js实现鼠标跟随运动效果
Aug 02 Javascript
微信小程序动态增加按钮组件
Sep 14 Javascript
Vue多组件仓库开发与发布详解
Feb 28 Javascript
微信小程序 组件的外部样式externalClasses使用详解
Sep 06 Javascript
JS实现购物车基本功能
Nov 08 Javascript
react-native封装插件swiper的使用方法
Mar 20 #Javascript
在vue项目中使用sass的配置方法
Mar 20 #Javascript
webpack vue项目开发环境局域网访问方法
Mar 20 #Javascript
动态加载、移除js/css文件的示例代码
Mar 20 #Javascript
webpack 打包压缩js和css的方法示例
Mar 20 #Javascript
浅谈Node 调试工具入门教程
Mar 20 #Javascript
使用Vue.js开发微信小程序开源框架mpvue解析
Mar 20 #Javascript
You might like
用php解析html的实现代码
2011/08/08 PHP
我整理的PHP 7.0主要新特性
2016/01/07 PHP
Yii2实现多域名跨域同步登录退出
2017/02/04 PHP
Laravel框架实现抢红包功能示例
2019/10/31 PHP
JS控件autocomplete 0.11演示及下载 1月5日已更新
2007/01/09 Javascript
JavaScript 监听textarea中按键事件
2009/10/08 Javascript
JSONP 跨域访问代理API-yahooapis实现代码
2012/12/02 Javascript
javascript中window.event事件用法详解
2012/12/11 Javascript
JavaScript子类用Object.getPrototypeOf去调用父类方法解析
2013/12/05 Javascript
jQuery实现div浮动层跟随页面滚动效果
2014/02/11 Javascript
理解Javascript的动态语言特性
2015/06/17 Javascript
js实现模拟银行卡账号输入显示效果
2015/11/18 Javascript
JS树形菜单组件Bootstrap TreeView使用方法详解
2016/12/21 Javascript
微信小程序 支付功能开发错误总结
2017/02/21 Javascript
Angularjs使用指令做表单校验的方法
2017/03/31 Javascript
基于Bootstrap模态对话框只加载一次 remote 数据的解决方法
2017/07/09 Javascript
利用Vue实现移动端图片轮播组件的方法实例
2017/08/23 Javascript
微信小程序 input输入及动态设置按钮的实现
2017/10/27 Javascript
vue-router命名视图的使用讲解
2019/01/19 Javascript
react native 原生模块桥接的简单说明小结
2019/02/26 Javascript
iview form清除校验状态的实现
2019/09/19 Javascript
关于vue表单提交防双/多击的例子
2019/10/31 Javascript
微信小程序:报错(in promise) MiniProgramError
2020/10/30 Javascript
[33:28]完美世界DOTA2联赛PWL S3 PXG vs GXR 第三场 12.19
2020/12/24 DOTA
Windows系统下使用flup搭建Nginx和Python环境的方法
2015/12/25 Python
django admin 根据choice字段选择的不同来显示不同的页面方式
2020/05/13 Python
python如何遍历指定路径下所有文件(按按照时间区间检索)
2020/09/14 Python
如何创建一个Flask项目并进行简单配置
2020/11/18 Python
利用python如何实现猫捉老鼠小游戏
2020/12/04 Python
仪器仪表检测毕业生自荐信
2013/10/31 职场文书
化学教学随笔感言
2014/02/19 职场文书
公司活动方案范文
2014/03/06 职场文书
2014预防青少年违法犯罪工作总结
2014/12/10 职场文书
刘胡兰观后感
2015/06/16 职场文书
详解python的内存分配机制
2021/05/10 Python
Requests什么的通通爬不了的Python超强反爬虫方案!
2021/05/20 Python