请实现一个LRU缓存函数?

167 阅读3分钟

手写一个 LRU 缓存函数

ONR~W97A8ZM.png

百度百科解释:

LRULeast Recently Used 的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。

LRU Cache通俗来讲,即”最不重要的且最不经常使用的数据,应优先删除“(内存空间是有限的)。

常见例子:
  • 当用户访问不同站点,浏览器会缓存对应站点的一些信息,在下次访问同一站点,直接从缓存中获取,就可以实现使访问速度变快
  • 浏览器的最近浏览记录存储
例子实现:

实现浏览器最近浏览记录的存储

需要注意的点:

  • 访问内存中已存在的网址,需要重新更新该网址在内存中的存储顺序?

    • 需要
  • 没有数据的时候是否需要执行删除操作?

    • 不需要而且不用担心(后面会解释)
  • 内置方法应该用哪种: putpostdeleteget

      • put 通常用于修改数据,该请求向服务器端发送数据,从而改变信息,就像数据库的update操作一样,用来修改数据的内容,但是不会增加数据的种类等,也就是说无论进行多少次PUT操作,其结果并没有不同。(当然,你也可以尝试一下拿 put 来创建数据)
      • post 通常用于创建数据,与put 的区别:POST主要作用在一个集合资源之上的(url(统一资源定位符)),而PUT主要作用在一个具体资源之上的(url/xxx),通俗一下讲就是,如URL可以在客户端确定,那么可使用PUT,否则用POST
      • delete 通常用于删除数据
      • get 通常用于获取数据(通常是幂等性 ==> 多次执行而不会改变系统状态(不会对数据进行修改),确保改变网站的一致性)
    • 为了保证规范性,实现浏览器最近浏览记录的存储 中获取 URL 可以用 GET ,修改 URL 可以用 PUT
需求分析:
  • 数据往内存的插入或使用顺序应该是有序的 (不用 Object
  • 性能问题,即用O(1)时间复杂度实现缓存的读取,即不能去遍历所有数据(排除 Array
  • 数据的重复性?(不用 Set

排除 Object ,因为 Object 遍历 key 值时,是无序的(symbol 会按插入先后的顺序,而stringnumber会按ASCII码大小 ...)

O%CPSALB)AR7S@M}5}BU0BP.png 所以用 ES6Map 来实现,Map 的特性:新插入的数据排在后面,旧数据放在前面,则:

  • 删除数据,则优先从前面删除

  • 读取数据,需要把该数据放在后面变成常用的数据

    • 可以通过先删除该数据,然后在添加该数据实现
代码实现

初始化缓存函数:

class LRUCache {
    constructor(n) {
        this.size = n // 初始化最大缓存数据条为n
        this.cacheMap = new Map() // 初始化缓存空间map
    },
    put(domain, info) {},
    get(domain) {}
}
  • 要注意的是,如果URL已存在,先移除数据,再更新数据,需遵守最大值不超过 n 的规则

最终实现:

class LRUCache {
    constructor(n) {
        this.size = n // 初始化最大缓存数据条为n
        this.cacheMap = new Map() // 初始化缓存空间map
    }
    put(domain, info) {
        if(this.cacheMap.has(domain)) {
            // 已存在需更新数据的位置
            this.cacheMap.delete(domain); //移除数据
        }
        if(this.cacheMap.size >= this.size) {
            // 删除最不常用数据(Map.keys()返回一个迭代器 function* )
            const firstKey = this.cacheMap.keys().next().value; // 不必当心cacheMap为空,因为this.size一般不为空
            // [...this.cacheMap.keys()][0] // 写法2
            this.cacheMap.delete(firstKey);
        }
        this.cacheMap.set(domain, info); // 在末尾重新插入数据
    }
    get(domain) {
        if(!this.cacheMap.has(domain)) return false;
        const info = this.cacheMap.get(domain); // 获取结果
        this.cacheMap.delete(domain); // 移除数据
        this.cacheMap.set(domain, info); // 在末尾重新插入数据
        return info; 
    }
}