缓存优化利器:5分钟实现 LRU Cache,从原理到代码!

简介: 嗨,大家好!我是你们的技术小伙伴——小米。今天带大家深入了解并手写一个实用的LRU Cache(最近最少使用缓存)。LRU Cache是一种高效的数据淘汰策略,在内存有限的情况下特别有用。本文将从原理讲起,带你一步步用Java实现一个简单的LRU Cache,并探讨其在真实场景中的应用与优化方案,如线程安全、缓存持久化等。无论你是初学者还是有一定经验的开发者,都能从中受益。让我们一起动手,探索LRU Cache的魅力吧!别忘了点赞、转发和收藏哦~



嘿,大家好,我是你们的技术分享小伙伴——小米!今天给大家带来一个超级实用的算法实现——手写LRU Cache。大家在日常开发中,可能经常会遇到需要缓存的场景,而LRU(Least Recently Used)Cache 是一种非常高效的缓存淘汰策略。这篇文章将从理论讲解到代码实现,手把手教你写一个简单的 LRU Cache!

目录

  • LRU Cache 简介
  • 实现思路解析
  • 手写 LRU Cache 代码(Java 实现)
  • 代码讲解与分析
  • 扩展与优化

LRU Cache 简介

LRU(Least Recently Used)算法的核心思想是:最近使用的数据将被保留,最久未使用的数据将被淘汰。这种策略适用于内存有限、但又需要高频访问的数据场景,比如缓存系统、页面置换算法等。

简单来说,LRU Cache 会维护一个固定大小的缓存,每次访问数据时:

  • 如果数据已经在缓存中,将其提升为“最近使用”;
  • 如果数据不在缓存中,则将其插入缓存中;
  • 如果缓存已满,会淘汰最久未使用的数据。

这种机制可以有效避免缓存中存放的数据很久没有被使用,从而浪费内存空间。

实现思路解析

LRU Cache 的核心需求有两个:

  • 快速访问数据: O(1) 时间复杂度。
  • 快速更新访问顺序: 当某个数据被访问时,需要将其提升为最近使用的数据。

要实现以上功能,最直接的想法就是结合 哈希表双向链表

  • 哈希表(HashMap): 提供 O(1) 时间复杂度的数据查找。
  • 双向链表: 提供 O(1) 时间复杂度的插入、删除操作,保证缓存顺序的维护。

双向链表的设计思路是:每次访问或插入数据时,将该数据节点移到链表头部;如果缓存满了,则淘汰链表尾部的节点。

手写 LRU Cache 代码(Java 实现)

好了,废话不多说,直接上代码吧!下面我会用 Java 来手写一个 LRU Cache。

代码讲解与分析

这个手写的 LRU Cache 实现里,我们使用了双向链表来维护缓存的数据顺序,用 HashMap 来实现 O(1) 时间复杂度的查找。

主要实现逻辑:

  • 构造函数:
  • 初始化一个 capacity 表示缓存的容量;
  • 使用 HashMap 存储缓存的数据,键为 K,值为对应的链表节点;
  • 初始化双向链表的 headtail 哨兵节点,方便管理节点的插入与删除。
  • get 方法:
  • HashMap 中查找是否存在该 key
  • 如果存在,调用 moveToHead 方法将该节点移动到链表头部;
  • 如果不存在,返回 null 表示缓存未命中。
  • put 方法:
  • 如果该 key 已经存在于缓存中,更新其值并将其移动到链表头部;
  • 如果该 key 不存在,创建一个新节点,并将其加入链表头部;
  • 如果缓存已满,移除链表尾部的节点(即最久未使用的节点),并从 HashMap 中删除该节点。
  • moveToHead 方法:
  • 先从链表中移除该节点,再将其插入到链表头部,标记为最近使用的节点。
  • removeTail 方法:
  • 直接移除链表的尾部节点,并返回该节点。尾部节点即是最久未使用的节点。

扩展与优化

  • 线程安全:目前这个 LRU Cache 版本是非线程安全的。如果你的应用场景涉及多线程环境,可以考虑在 getput 方法上加锁,或者使用 ConcurrentHashMap 来替代 HashMap,配合 ReentrantLock 保证线程安全。
  • 缓存持久化:在某些应用场景下,你可能需要缓存的持久化存储。可以将 LRUCache 结合 Redis、文件系统等,实现持久化缓存,防止缓存数据丢失。
  • 缓存容量动态调整:可以扩展 LRUCache 的功能,使其支持动态调整缓存容量,方便应对不同的场景需求。

总结

今天我们一起动手实现了一个简易版的 LRU Cache,通过双向链表和哈希表的组合,保证了缓存操作的高效性。希望这篇文章能帮助大家更好地理解 LRU 算法的核心思想,并能应用到实际开发中。

如果大家有任何疑问或者希望我讲解其他技术点,欢迎留言交流哦!我们下期再见啦~

点赞、转发、收藏一波,支持小米继续写作~

小米的小Tips

  • LRU Cache 是面试中的经典问题,不仅考察算法能力,还考察数据结构的运用。
  • 平时多动手写一写,有助于更好地掌握知识点!

期待大家的反馈和建议,我会根据大家的需求推出更多有趣的技术文章!

我是小米,一个喜欢分享技术的29岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号软件求生,获取更多技术干货!

相关文章
|
12天前
|
存储 缓存 小程序
微信小程序数据缓存与本地存储:优化用户体验
本文深入探讨微信小程序的数据缓存与本地存储,介绍其意义、机制及应用场景。通过合理使用内存和本地缓存,可减少网络请求、提升加载速度和用户体验。文中详细讲解了常用缓存API的使用方法,并通过一个新闻列表案例展示了缓存的实际应用。最后提醒开发者注意缓存大小限制、时效性和清理,以确保最佳性能。
|
3月前
|
存储 缓存 自然语言处理
SCOPE:面向大语言模型长序列生成的双阶段KV缓存优化框架
KV缓存是大语言模型(LLM)处理长文本的关键性能瓶颈,现有研究多聚焦于预填充阶段优化,忽视了解码阶段的重要性。本文提出SCOPE框架,通过分离预填充与解码阶段的KV缓存策略,实现高效管理。SCOPE保留预填充阶段的关键信息,并在解码阶段引入滑动窗口等策略,确保重要特征的有效选取。实验表明,SCOPE仅用35%原始内存即可达到接近完整缓存的性能水平,显著提升了长文本生成任务的效率和准确性。
258 3
SCOPE:面向大语言模型长序列生成的双阶段KV缓存优化框架
|
4月前
|
缓存 监控 前端开发
在资源加载优化中,如何利用浏览器缓存提升性能?
通过以上这些方法,可以有效地利用浏览器缓存来提升资源加载的性能,减少网络请求次数,提高用户体验和应用的响应速度。同时,需要根据具体的应用场景和资源特点进行灵活调整和优化,以达到最佳的效果。此外,随着技术的不断发展和变化,还需要持续关注和学习新的缓存优化方法和策略。
121 53
|
4月前
|
缓存 监控 测试技术
如何利用浏览器的缓存来优化网站性能?
【10月更文挑战第23天】通过以上多种方法合理利用浏览器缓存,可以显著提高网站的性能,减少网络请求,加快资源加载速度,提升用户的访问体验。同时,要根据网站的具体情况和资源的特点,不断优化和调整缓存策略,以适应不断变化的业务需求和用户访问模式。
165 7
|
5月前
|
存储 缓存 分布式计算
大数据-89 Spark 集群 RDD 编程-高阶 编写代码、RDD依赖关系、RDD持久化/缓存
大数据-89 Spark 集群 RDD 编程-高阶 编写代码、RDD依赖关系、RDD持久化/缓存
78 4
|
5月前
|
缓存 JavaScript 前端开发
Vue 3的事件监听缓存如何优化性能?
【10月更文挑战第5天】随着前端应用复杂度的增加,性能优化变得至关重要。Vue 3 通过引入事件监听缓存等新特性提升了应用性能。本文通过具体示例介绍这一特性,解释其工作原理及如何利用它优化性能。与 Vue 2 相比,Vue 3 可在首次渲染时注册事件监听器并在后续渲染时重用,避免重复注册导致的资源浪费和潜在内存泄漏问题。通过使用 `watchEffect` 或 `watch` 监听状态变化并更新监听器,进一步提升应用性能。事件监听缓存有助于减少浏览器负担,特别在大型应用中效果显著,使应用更加流畅和响应迅速。
183 1
|
5月前
|
存储 缓存 监控
HTTP:强缓存优化实践
HTTP强缓存是提升网站性能的关键技术之一。通过精心设计缓存策略,不仅可以显著减少网络延迟,还能降低服务器负载,提升用户体验。实施上述最佳实践,结合持续的监控与调整,能够确保缓存机制高效且稳定地服务于网站性能优化目标。
85 3
|
3天前
|
缓存 NoSQL Java
Redis应用—8.相关的缓存框架
本文介绍了Ehcache和Guava Cache两个缓存框架及其使用方法,以及如何自定义缓存。主要内容包括:Ehcache缓存框架、Guava Cache缓存框架、自定义缓存。总结:Ehcache适合用作本地缓存或与Redis结合使用,Guava Cache则提供了更灵活的缓存管理和更高的并发性能。自定义缓存可以根据具体需求选择不同的数据结构和引用类型来实现特定的缓存策略。
Redis应用—8.相关的缓存框架
|
1月前
|
缓存 NoSQL 中间件
Redis,分布式缓存演化之路
本文介绍了基于Redis的分布式缓存演化,探讨了分布式锁和缓存一致性问题及其解决方案。首先分析了本地缓存和分布式缓存的区别与优劣,接着深入讲解了分布式远程缓存带来的并发、缓存失效(穿透、雪崩、击穿)等问题及应对策略。文章还详细描述了如何使用Redis实现分布式锁,确保高并发场景下的数据一致性和系统稳定性。最后,通过双写模式和失效模式讨论了缓存一致性问题,并提出了多种解决方案,如引入Canal中间件等。希望这些内容能为读者在设计分布式缓存系统时提供有价值的参考。感谢您的阅读!
130 6
Redis,分布式缓存演化之路
|
3月前
|
存储 缓存 NoSQL
解决Redis缓存数据类型丢失问题
解决Redis缓存数据类型丢失问题
205 85