Fancy DSA Fancy DSA
数据结构
算法
LeetCode
  • 关于
  • 导航 (opens new window)
  • 分类
  • 标签
  • 归档
设计模式 (opens new window)
博客 (opens new window)
GitHub (opens new window)

Jonsam NG

想的更多,也要想的更远
数据结构
算法
LeetCode
  • 关于
  • 导航 (opens new window)
  • 分类
  • 标签
  • 归档
设计模式 (opens new window)
博客 (opens new window)
GitHub (opens new window)
  • 开始上手
  • Plan 计划
  • Roadmap 路线
  • 算法简介
  • Sort 排序

  • Search 搜索

  • Recursive 递归

  • Graph 图

  • Tree 树

  • Math 数学

  • Hash 哈希

  • String 字符串

  • BitManipulation 位操纵

  • Backtracking 回溯

  • DynamicProgramming 动态规划

  • Cache 缓存

    • LFUCache [最近最少使用缓存]
    • LRUCache [最近最久未使用缓存]
      • 介绍
      • 实现
      • 参考
    • Memoize [缓存函数]
  • Array 数组

  • Ciphers 密码学

  • Conversions 转换

  • ProjectEuler 欧拉计划

  • 其他

  • 算法
  • Cache 缓存
jonsam
2022-09-26
目录

LRUCache [最近最久未使用缓存]

# 介绍

LRU(The Least Recently Used,最近最久未使用算法)是一种常见的缓存算法,在很多分布式缓存系统(如 Redis, Memcached)中都有广泛使用。LRU 算法的思想是:如果一个数据在最近一段时间没有被访问到,那么可以认为在将来它被访问的可能性也很小。因此,当空间满时,最久没有访问的数据最先被置换(淘汰)。

LRU 算法的描述: 设计一种缓存结构,该结构在构造时确定大小,假设大小为 K,并有两个功能:

  • set (key,value):将记录 (key,value) 插入该结构。当缓存满时,将最久未使用的数据置换掉。
  • get (key):返回 key 对应的 value 值。

实现:最朴素的思想就是用数组 + 时间戳的方式,不过这样做效率较低。因此,我们可以用双向链表(LinkedList)+ 哈希表(HashMap)实现(链表用来表示位置,哈希表用来存储和查找),在 Java 里有对应的数据结构 LinkedHashMap。

利用 Java 的 LinkedHashMap 用非常简单的代码来实现基于 LRU 算法的 Cache 功能。

class LRUCache extends LinkedHashMap<Integer,Integer>{
    private int capacity;
    public LRUCache(int capacity) {
        //调用父类中的构造方法创建一个LinkedHashMap,设置其容量为capacity,loadFactor为0.75,并开启accessOrder为true
        super(capacity, 0.75F, true);
        this.capacity = capacity;
    }

    public int get(int key) {
        //若key存在,返回对应value值;若key不存在,返回-1
        return super.getOrDefault(key,-1);
    }
    
    public void put(int key, int value) {
        super.put(key,value);
    }
    protected boolean removeEldestEntry(Map.Entry<Integer,Integer> eldest){
        //若返回的结果为true,则执行removeEntryForKey方法删除eldest entry
        return size() > capacity;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 实现

# JavaScript

class LRUCache {
  // LRU Cache to store a given capacity of data
  #capacity

  /**
   * @param {number} capacity - the capacity of LRUCache
   * @returns {LRUCache} - sealed
   */
  constructor (capacity) {
    if (!Number.isInteger(capacity) || capacity < 0) {
      throw new TypeError('Invalid capacity')
    }

    this.#capacity = ~~capacity
    this.misses = 0
    this.hits = 0
    this.cache = new Map()

    return Object.seal(this)
  }

  get info () {
    return Object.freeze({
      misses: this.misses,
      hits: this.hits,
      capacity: this.capacity,
      size: this.size
    })
  }

  get size () {
    return this.cache.size
  }

  get capacity () {
    return this.#capacity
  }

  set capacity (newCapacity) {
    if (newCapacity < 0) {
      throw new RangeError('Capacity should be greater than 0')
    }

    if (newCapacity < this.capacity) {
      let diff = this.capacity - newCapacity

      while (diff--) {
        this.#removeLeastRecentlyUsed()
      }
    }

    this.#capacity = newCapacity
  }

  /**
 * delete oldest key existing in map by the help of iterator
 */
  #removeLeastRecentlyUsed () {
    this.cache.delete(this.cache.keys().next().value)
  }

  /**
   * @param {string} key
   * @returns {*}
   */
  has (key) {
    key = String(key)

    return this.cache.has(key)
  }

  /**
   * @param {string} key
   * @param {*} value
   */
  set (key, value) {
    key = String(key)
    // Sets the value for the input key and if the key exists it updates the existing key
    if (this.size === this.capacity) {
      this.#removeLeastRecentlyUsed()
    }

    this.cache.set(key, value)
  }

  /**
   * @param {string} key
   * @returns {*}
   */
  get (key) {
    key = String(key)
    // Returns the value for the input key. Returns null if key is not present in cache
    if (this.cache.has(key)) {
      const value = this.cache.get(key)

      // refresh the cache to update the order of key
      this.cache.delete(key)
      this.cache.set(key, value)

      this.hits++
      return value
    }

    this.misses++
    return null
  }

  /**
   * @param {JSON} json
   * @returns {LRUCache}
   */
  parse (json) {
    const { misses, hits, cache } = JSON.parse(json)

    this.misses += misses ?? 0
    this.hits += hits ?? 0

    for (const key in cache) {
      this.set(key, cache[key])
    }

    return this
  }

  /**
   * @param {number} indent
   * @returns {JSON} - string
   */
  toString (indent) {
    const replacer = (_, value) => {
      if (value instanceof Set) {
        return [...value]
      }

      if (value instanceof Map) {
        return Object.fromEntries(value)
      }

      return value
    }

    return JSON.stringify(this, replacer, indent)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144

# 参考

  • LRU & LFU 缓存机制的原理及实现 - 知乎 (opens new window)
编辑 (opens new window)
上次更新: 2022/10/28, 17:23:56
LFUCache [最近最少使用缓存]
Memoize [缓存函数]

← LFUCache [最近最少使用缓存] Memoize [缓存函数]→

最近更新
01
0-20题解
10-31
02
本章导读
10-31
03
算法与转换:Part1
10-28
更多文章>
Theme by Vdoing | Copyright © 2022-2022 Fancy DSA | Made by Jonsam by ❤
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式