0

0

c++如何实现LRU缓存算法_c++ LRU设计与实现【源码】

冰火之心

冰火之心

发布时间:2026-02-01 18:48:08

|

114人浏览过

|

来源于php中文网

原创

用std::list+std::unordered_map实现O(1)LRU缓存的关键是:用map映射key到list迭代器,通过splice快速移动节点至头部,淘汰时取back()并同步更新map;需注意splice参数合法性、迭代器有效性、put时的更新/插入逻辑顺序及线程安全限制。

c++如何实现lru缓存算法_c++ lru设计与实现【源码】

std::list + std::unordered_map 实现 O(1) LRU

LRU 缓存的核心难点是「快速定位最久未用项」和「频繁移动访问项到最近位置」,C++ 标准库里没有现成的双向链表+哈希混合结构,但可以用 std::list 存节点顺序、std::unordered_map 做键到链表迭代器的映射,两者配合达成插入、查询、更新全 O(1)。

关键点在于:不能把值直接存在 std::list 里再靠遍历找——那会退化成 O(n);必须让 map 的 value 是 std::list::iterator,这样通过 key 一查就知道它在链表哪,splice 一下就能移到头部。

  • std::listpush_front 插新项,用 splice 把已有节点提到头部(不是 erase + push,避免重复构造)
  • std::unordered_map 的 key 是缓存 key,value 是对应 std::list<:pair v>>::iterator
  • 淘汰时直接取 list.back(),然后从 map 中 erase 对应 key

注意 std::list::splice 的参数陷阱

很多人写 cache.splice(cache.begin(), cache, it) 想把 it 移到开头,结果运行时报错或行为异常——这是因为 splice 要求两个 list 是同一个对象,而传入的 it 必须属于当前 list。更隐蔽的问题是:如果用 it 之后又用了 map 的迭代器(比如 erase),it 可能失效(虽然 std::list 迭代器不因插入/删除失效,但 erase map 后若误用旧 it 就危险)。

  • 安全写法:先 auto it_list = map.at(key) 拿到链表迭代器,再 cache.splice(cache.begin(), cache, it_list)
  • 不要在 splice 后继续用原 it_list 做其他操作,除非确认没被 invalidate(其实一般不会,但逻辑上建议用完即弃)
  • 如果用 C++11 之前的编译器,splice 不支持三参数形式,得用四参数:cache.splice(cache.begin(), cache, it_list, std::next(it_list))

构造函数和容量控制要提前处理 key 冲突

LRU 缓存初始化时指定容量 capacity,但很多人忽略:当 put(k, v) 时,如果 k 已存在,应该更新值并提升优先级,而不是当成新 key 处理;如果不存在且缓存已满,才淘汰尾部再插入。这个判断顺序错了就会导致容量失控或重复插入。

讯飞绘文
讯飞绘文

讯飞绘文:免费AI写作/AI生成文章

下载

立即学习C++免费学习笔记(深入)”;

  • 每次 put 先查 map.find(key) != map.end():存在则 splice + 更新 value;不存在再看 size 是否已达 capacity
  • 淘汰前务必先 map.erase(list.back().first),再 list.pop_back(),顺序反了会导致 dangling iterator
  • 别在构造函数里预分配 list 节点——LRU 是按需增长的,预填会干扰访问序

线程安全不是默认选项,别想当然加锁

标准 std::liststd::unordered_map 都不保证并发读写安全。如果你的 LRU 会被多线程调用,不能只给成员函数加 std::mutex 就完事——比如 get 中查 map、取迭代器、访问 list 三个动作必须原子,否则可能拿到迭代器后 list 被别的线程 splice 走,导致解引用崩溃。

  • 最简方案:整个操作用一个 mutable std::mutex mtx 包住,所有 public 方法加 std::lock_guard
  • 高并发场景慎用:锁粒度太大,getput 会互相阻塞,此时考虑用分段锁或无锁结构(如基于 hazard pointer 的实现),但复杂度陡增
  • 别依赖 std::shared_mutex 做读写锁优化——因为 get 实际上也要写(要 splice),所以读多写少的优势基本不存在

真正难的不是写对单线程版,而是想清楚哪些操作必须串行、哪些可以分离。比如 key 查找和 value 构造可以挪到锁外,但链表结构调整和 map 更新必须一起锁住。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

546

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

210

2025.12.24

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

20

2026.01.21

C++多线程相关合集
C++多线程相关合集

本专题整合了C++多线程相关教程,阅读专题下面的的文章了解更多详细内容。

19

2026.01.21

golang map内存释放
golang map内存释放

本专题整合了golang map内存相关教程,阅读专题下面的文章了解更多相关内容。

75

2025.09.05

golang map相关教程
golang map相关教程

本专题整合了golang map相关教程,阅读专题下面的文章了解更多详细内容。

36

2025.11.16

golang map原理
golang map原理

本专题整合了golang map相关内容,阅读专题下面的文章了解更多详细内容。

61

2025.11.17

java判断map相关教程
java判断map相关教程

本专题整合了java判断map相关教程,阅读专题下面的文章了解更多详细内容。

42

2025.11.27

go语言 注释编码
go语言 注释编码

本专题整合了go语言注释、注释规范等等内容,阅读专题下面的文章了解更多详细内容。

30

2026.01.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
10分钟--Midjourney创作自己的漫画
10分钟--Midjourney创作自己的漫画

共1课时 | 0.1万人学习

Midjourney 关键词系列整合
Midjourney 关键词系列整合

共13课时 | 0.9万人学习

AI绘画教程
AI绘画教程

共2课时 | 0.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号