文章

LRU 缓存算法

LRU 缓存算法

我们在使用安卓手机的时候,上划屏幕打开的任务栏中,排最前面的后台任务永远是最近我们使用过的, 而那些很久没被使用的后台任务会被放到最后,这种功能就可以使用 LRU 算法来实现。

什么是 LRU 算法?

LRU(Least Recently Used)是一种缓存淘汰策略,其核心思想是移除最近最少使用的数据,保留最近访问的数据。

代码实现

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
#include <list>
#include <unordered_map>

template<typename KeyType, typename ValueType>
class LRUCache {
private:
    size_t m_capacity;  // 缓存容量
    std::list<std::pair<KeyType, ValueType>> m_cache_list;  // 双向链表存储键值对
    std::unordered_map<KeyType, typename std::list<std::pair<KeyType, ValueType>>::iterator> m_cache_map;  // 哈希表映射

public:
    // 显式构造函数,避免隐式转换
    explicit LRUCache(size_t capacity) 
        : m_capacity(capacity) {}

    // 查询操作(返回bool表示是否存在,通过引用返回value)
    bool get(const KeyType& key, ValueType& value) {
        auto it = m_cache_map.find(key);
        if (it == m_cache_map.end()) {
            return false;  // 键不存在
        }
        // 移动节点到链表头部
        m_cache_list.splice(m_cache_list.begin(), m_cache_list, it->second);
        value = it->second->second;  // 通过引用返回值
        return true;
    }

    // 插入/更新操作
    void put(const KeyType& key, const ValueType& value) {
        auto it = m_cache_map.find(key);
        if (it != m_cache_map.end()) {
            // 更新值并移动到头部
            it->second->second = value;
            m_cache_list.splice(m_cache_list.begin(), m_cache_list, it->second);
            return;
        }
        // 插入新节点到头部
        m_cache_list.emplace_front(key, value);
        m_cache_map[key] = m_cache_list.begin();
        // 容量检查,删除尾部节点
        if (m_cache_map.size() > m_capacity) {
            auto last_key = m_cache_list.back().first;
            m_cache_map.erase(last_key);
            m_cache_list.pop_back();
        }
    }

    // 检查键是否存在
    bool contains(const KeyType& key) const {
        return m_cache_map.find(key) != m_cache_map.end();
    }

    // 获取当前缓存大小
    size_t size() const {
        return m_cache_map.size();
    }
};

在上面的代码中,可以看到,为了避免每次都去遍历 list 容器,所以采用 unordered_map 无需容器来缓存每个 key 对应的 list 迭代器,从而提升访问速度。 下面是这个缓存算法的用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <string>

int main() {
  // 键类型为 int,值类型为 std::string
  LRUCache<int, std::string> cache(3);
  // 插入数据
  cache.put(1, "One");
  cache.put(2, "Two");
  cache.put(3, "Three");
  // 查询数据
  std::string value;
  if (cache.get(2, value)) { // 命中,value="Two"
    std::cout << "Value of key 2: " << value << std::endl;
  }
  // 触发淘汰
  cache.put(4, "Four"); // 插入第四个条目,淘汰最久未使用的键1
  // 检查键是否存在
  std::cout << "Contains key 1? " << cache.contains(1)
            << std::endl; // 输出0(false)
  return 0;
}
本文由作者按照 CC BY 4.0 进行授权

© xiongyi. 保留部分权利。

|

岂堪久蔽苍苍色,须放三光照九州