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

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

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 处理;如果不存在且缓存已满,才淘汰尾部再插入。这个判断顺序错了就会导致容量失控或重复插入。

  • 每次 put 先查 map.find(key) != map.end():存在则 splice + 更新 value;不存在再看 size 是否已达 capacit

    y
  • 淘汰前务必先 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  # c++  # 无锁  # 标准库  # red  # 成员函数  # 构造函数  # auto  # mutable  # public  # 线程  # 多线程  # pointer  # map  # 并发  # 对象  # 算法  # 迭代  # 链表  # 不存在  # 很多人  # 移到  # 就会  # 都不  # 就能  # 已有  # 可以用 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: Microsoft Edge如何解决网页加载问题 Edge浏览器加载问题修复  Laravel如何处理和验证JSON类型的数据库字段  如何在HTML表单中获取用户输入并结合JavaScript动态控制复利计算循环  html5如何设置样式_HTML5样式设置方法与CSS应用技巧【教程】  如何在阿里云购买域名并搭建网站?  JavaScript实现Fly Bird小游戏  Laravel策略(Policy)如何控制权限_Laravel Gates与Policies实现用户授权  Laravel如何使用Sanctum进行API认证?(SPA实战)  如何基于云服务器快速搭建个人网站?  如何确认建站备案号应放置的具体位置?  如何在 Python 中将列表项按字母顺序编号(a.、b.、c. …)  如何在阿里云高效完成企业建站全流程?  html如何与html链接_实现多个HTML页面互相链接【互相】  ChatGPT回答中断怎么办 引导AI继续输出完整内容的方法  Laravel如何实现一对一模型关联?(Eloquent示例)  Laravel API路由如何设计_Laravel构建RESTful API的路由最佳实践  香港服务器如何优化才能显著提升网站加载速度?  bootstrap日历插件datetimepicker使用方法  高性能网站服务器部署指南:稳定运行与安全配置优化方案  深圳防火门网站制作公司,深圳中天明防火门怎么编码?  如何在服务器上三步完成建站并提升流量?  怎么用AI帮你设计一套个性化的手机App图标?  Laravel API资源(Resource)怎么用_格式化Laravel API响应的最佳实践  手机怎么制作网站教程步骤,手机怎么做自己的网页链接?  如何在景安服务器上快速搭建个人网站?  jQuery 常见小例汇总  Python制作简易注册登录系统  如何快速生成橙子建站落地页链接?  高性价比服务器租赁——企业级配置与24小时运维服务  深圳网站制作设计招聘,关于服装设计的流行趋势,哪里的资料比较全面?  Laravel如何实现本地化和多语言支持_Laravel多语言配置与翻译文件管理  Laravel如何实现RSS订阅源功能_Laravel动态生成网站XML格式订阅内容【教程】  极客网站有哪些,DoNews、36氪、爱范儿、虎嗅、雷锋网、极客公园这些互联网媒体网站有什么差异?  Laravel Eloquent访问器与修改器是什么_Laravel Accessors & Mutators数据处理技巧  Laravel如何实现数据库事务?(DB Facade示例)  Laravel如何使用.env文件管理环境变量?(最佳实践)  Laravel如何实现图片防盗链功能_Laravel中间件验证Referer来源请求【方案】  如何在阿里云虚拟主机上快速搭建个人网站?  php中::能调用final静态方法吗_final修饰静态方法调用规则【解答】  网站制作价目表怎么做,珍爱网婚介费用多少?  Laravel如何自定义分页视图?(Pagination示例)  5种Android数据存储方式汇总  Laravel的Blade指令怎么自定义_创建你自己的Laravel Blade Directives  Laravel怎么定时执行任务_Laravel任务调度器Schedule配置与Cron设置【教程】  香港服务器部署网站为何提示未备案?  Laravel如何安装使用Debugbar工具栏_Laravel性能调试与SQL监控插件【步骤】  Laravel Seeder怎么填充数据_Laravel数据库填充器的使用方法与技巧  Laravel怎么进行数据库事务处理_Laravel DB Facade事务操作确保数据一致性  制作ppt免费网站有哪些,有哪些比较好的ppt模板下载网站?  桂林网站制作公司有哪些,桂林马拉松怎么报名?