1+ /*
2+ * Copyright (c) 2026 hikyuu.org
3+ *
4+ * Created on: 2026-01-18
5+ * Author: fasiondog
6+ *
7+ * 注:该代码由 AI 生成
8+ */
9+
10+ #pragma once
11+ #include < unordered_map>
12+ #include < list>
13+ #include < shared_mutex>
14+ #include < mutex>
15+
16+ namespace hku {
17+
18+ /* *
19+ * @brief LRU (Least Recently Used) 缓存实现
20+ *
21+ * 该缓存实现基于哈希表和双向链表,能够在O(1)时间内完成插入、删除和访问操作。
22+ * 当缓存达到容量限制时,会自动淘汰最久未使用的元素。
23+ *
24+ * 特性:
25+ * - 线程安全:使用共享互斥锁支持并发读写
26+ * - 移动语义:支持值的移动插入,避免不必要的拷贝
27+ * - 溢出容量:允许缓存临时超出设定容量,仅当达到容量+溢出容量时才触发淘汰
28+ * - 动态调整:支持运行时调整容量和溢出容量
29+ *
30+ * @tparam KeyType 键的类型,必须支持哈希和相等比较
31+ * @tparam ValueType 值的类型,必须支持拷贝和移动操作
32+ */
33+ template <typename KeyType, typename ValueType>
34+ class LruCache {
35+ public:
36+ using key_type = KeyType;
37+ using value_type = ValueType;
38+ using size_type = size_t ;
39+
40+ /* *
41+ * @brief 构造函数
42+ * @param capacity 缓存容量,默认为64,0表示无限制容量
43+ * @param overflow 溢出容量,默认为8,允许缓存临时超出设定容量
44+ * 仅当缓存大小 >= 容量+溢出容量时才触发淘汰机制
45+ */
46+ explicit LruCache (size_type capacity = 64 , size_type overflow = 8 )
47+ : m_capacity(capacity), m_overflow(overflow) {}
48+
49+ ~LruCache () = default ;
50+
51+ /* *
52+ * @brief 插入键值对
53+ * @param key 键
54+ * @param value 值
55+ */
56+ void insert (const key_type& key, const value_type& value) {
57+ std::unique_lock<std::shared_mutex> lock (m_mutex);
58+
59+ auto it = m_cache.find (key);
60+ if (it != m_cache.end ()) {
61+ // 更新已存在的键
62+ it->second ->second = value;
63+ m_lru_list.splice (m_lru_list.begin (), m_lru_list, it->second );
64+ } else {
65+ // 先尝试清理空间
66+ _prune_if_needed ();
67+
68+ m_lru_list.emplace_front (key, value);
69+ m_cache[key] = m_lru_list.begin ();
70+ }
71+ }
72+
73+ /* *
74+ * @brief 插入键值对(移动版本)
75+ * @param key 键
76+ * @param value 值(右值引用)
77+ */
78+ void insert (const key_type& key, value_type&& value) {
79+ std::unique_lock<std::shared_mutex> lock (m_mutex);
80+
81+ auto it = m_cache.find (key);
82+ if (it != m_cache.end ()) {
83+ // 更新已存在的键
84+ it->second ->second = std::forward<value_type>(value);
85+ m_lru_list.splice (m_lru_list.begin (), m_lru_list, it->second );
86+ } else {
87+ // 先尝试清理空间
88+ _prune_if_needed ();
89+
90+ m_lru_list.emplace_front (key, std::forward<value_type>(value));
91+ m_cache[key] = m_lru_list.begin ();
92+ }
93+ }
94+
95+ /* *
96+ * @brief 获取键对应的值
97+ * @param key 键
98+ * @return 存在则返回值,否则返回ValueType的默认构造值
99+ */
100+ value_type get (const key_type& key) {
101+ std::shared_lock<std::shared_mutex> lock (m_mutex);
102+
103+ auto it = m_cache.find (key);
104+ if (it != m_cache.end ()) {
105+ // 移动到列表头部(标记为最近使用)
106+ m_lru_list.splice (m_lru_list.begin (), m_lru_list, it->second );
107+ return it->second ->second ;
108+ }
109+
110+ return value_type{};
111+ }
112+
113+ /* *
114+ * @brief 尝试获取键对应的值
115+ * @param key 键
116+ * @param value 用于接收值的引用参数
117+ * @return 如果键存在返回true,否则返回false
118+ */
119+ bool tryGet (const key_type& key, value_type& value) {
120+ std::shared_lock<std::shared_mutex> lock (m_mutex);
121+
122+ auto it = m_cache.find (key);
123+ if (it != m_cache.end ()) {
124+ // 移动到列表头部(标记为最近使用)
125+ m_lru_list.splice (m_lru_list.begin (), m_lru_list, it->second );
126+ value = it->second ->second ;
127+ return true ;
128+ }
129+
130+ return false ;
131+ }
132+
133+ /* *
134+ * @brief 检查是否包含指定键
135+ * @param key 键
136+ * @return 存在返回true,否则返回false
137+ */
138+ bool contains (const key_type& key) {
139+ std::shared_lock<std::shared_mutex> lock (m_mutex);
140+ return m_cache.find (key) != m_cache.end ();
141+ }
142+
143+ /* *
144+ * @brief 删除指定键
145+ * @param key 要删除的键
146+ * @return 成功删除返回true,不存在返回false
147+ */
148+ bool remove (const key_type& key) {
149+ std::unique_lock<std::shared_mutex> lock (m_mutex);
150+
151+ auto it = m_cache.find (key);
152+ if (it != m_cache.end ()) {
153+ m_lru_list.erase (it->second );
154+ m_cache.erase (it);
155+ return true ;
156+ }
157+ return false ;
158+ }
159+
160+ /* *
161+ * @brief 清空缓存
162+ */
163+ void clear () {
164+ std::unique_lock<std::shared_mutex> lock (m_mutex);
165+ m_cache.clear ();
166+ m_lru_list.clear ();
167+ }
168+
169+ /* *
170+ * @brief 获取缓存当前大小
171+ * @return 当前缓存元素数量
172+ */
173+ size_type size () const {
174+ std::shared_lock<std::shared_mutex> lock (m_mutex);
175+ return m_cache.size ();
176+ }
177+
178+ /* *
179+ * @brief 检查缓存是否为空
180+ * @return 空返回true,否则返回false
181+ */
182+ bool empty () const {
183+ std::shared_lock<std::shared_mutex> lock (m_mutex);
184+ return m_cache.empty ();
185+ }
186+
187+ /* *
188+ * @brief 获取缓存容量
189+ * @return 缓存容量
190+ */
191+ size_type capacity () const {
192+ return m_capacity;
193+ }
194+
195+ /* *
196+ * @brief 获取缓存溢出容量
197+ * @return 缓存溢出容量
198+ */
199+ size_type overflow () const {
200+ return m_overflow;
201+ }
202+
203+ /* *
204+ * @brief 设置缓存容量
205+ * @param capacity 新的容量,0表示不限制容量
206+ */
207+ void resize (size_type capacity) {
208+ std::unique_lock<std::shared_mutex> lock (m_mutex);
209+ m_capacity = capacity;
210+
211+ // 如果容量不为0且当前大小超过新容量加上溢出容量,需要移除多余的元素
212+ while (m_capacity != 0 && m_cache.size () > m_capacity + m_overflow) {
213+ auto last = m_lru_list.end ();
214+ --last;
215+ m_cache.erase (last->first );
216+ m_lru_list.pop_back ();
217+ }
218+ }
219+
220+ /* *
221+ * @brief 设置缓存溢出容量
222+ * @param overflow 新的溢出容量
223+ */
224+ void setOverflow (size_type overflow) {
225+ std::unique_lock<std::shared_mutex> lock (m_mutex);
226+ m_overflow = overflow;
227+
228+ // 如果容量不为0且当前大小超过新容量加上溢出容量,需要移除多余的元素
229+ while (m_capacity != 0 && m_cache.size () > m_capacity + m_overflow) {
230+ auto last = m_lru_list.end ();
231+ --last;
232+ m_cache.erase (last->first );
233+ m_lru_list.pop_back ();
234+ }
235+ }
236+
237+ private:
238+ // 如果缓存已满,移除最久未使用的项
239+ void _prune_if_needed () {
240+ if (m_capacity != 0 && m_cache.size () >= m_capacity + m_overflow) {
241+ // 移除最久未使用的项
242+ auto last = m_lru_list.end ();
243+ --last;
244+ m_cache.erase (last->first );
245+ m_lru_list.pop_back ();
246+ }
247+ }
248+
249+ private:
250+ // 存储键值对的双向链表,用于维护访问顺序
251+ std::list<std::pair<key_type, value_type>> m_lru_list;
252+
253+ // 哈希表,用于快速查找链表节点
254+ std::unordered_map<key_type, typename std::list<std::pair<key_type, value_type>>::iterator>
255+ m_cache;
256+
257+ // 读写锁,保护并发访问
258+ mutable std::shared_mutex m_mutex;
259+
260+ // 缓存容量,0表示不限制容量
261+ size_type m_capacity;
262+
263+ // 缓存溢出容量,在容量为0时不生效
264+ size_type m_overflow;
265+ };
266+
267+ } // namespace hku
0 commit comments