详解LRU缓存算法[通俗易懂]

详解LRU缓存算法

大家好,又见面了,我是全栈君。

一、什么是缓存

这里说的缓存是一种广义的概念,在计算机存储层次结构中,低一层的存储器都可以看做是高一层的缓存。比如Cache是内存的缓存,内存是硬盘的缓存,硬盘是网络的缓存等等。

缓存可以有效地解决存储器性能与容量的这对矛盾,但绝非看上去那么简单。如果缓存算法设计不当,非但不能提高访问速度,反而会使系统变得更慢。

从本质上来说,缓存之所以有效是因为程序和数据的局部性(locality)。程序会按固定的顺序执行,数据会存放在连续的内存空间并反复读写。这些特点使得我们可以缓存那些经常用到的数据,从而提高读写速度。

缓存的大小是固定的,它应该只保存最常被访问的那些数据。然而未来不可预知,我们只能从过去的访问序列做预测,于是就有了各种各样的缓存替换策略。本文介绍一种简单的缓存策略,称为最近最少使用(LRU,Least Recently Used)算法。

二、LRU的实现

我们以内存访问为例解释缓存的工作原理。假设缓存的大小固定,初始状态为空。每发生一次读内存操作,首先查找待读取的数据是否存在于缓存中,若是,则缓存命中,返回数据;若否,则缓存未命中,从内存中读取数据,并把该数据添加到缓存中。向缓存添加数据时,如果缓存已满,则需要删除访问时间最早的那条数据,这种更新缓存的方法就叫做LRU。

实现LRU时,我们需要关注它的读性能和写性能,理想的LRU应该可以在O(1)的时间内读取一条数据或更新一条数据,也就是说读写的时间复杂度都是O(1)。

此时很容易想到使用HashMap,根据数据的键访问数据可以达到O(1)的速度。但是更新缓存的速度却无法达到O(1),因为需要确定哪一条数据的访问时间最早,这需要遍历所有缓存才能找到。

因此,我们需要一种既按访问时间排序,又能在常数时间内随机访问的数据结构。

这可以通过HashMap+双向链表实现。HashMap保证通过key访问数据的时间为O(1),双向链表则按照访问时间的顺序依次穿过每个数据。之所以选择双向链表而不是单链表,是为了可以从中间任意结点修改链表结构,而不必从头结点开始遍历。

如下图所示,黑色部分为HashMap的结构,红色箭头则是双向链表的正向连接(逆向连接未画出)。可以清晰地看到,数据的访问顺序是1->3->5->6->10。我们只需要在每次访问过后改变链表的连接顺序即可。

详解LRU缓存算法[通俗易懂]

HashMap+双向链表

实现代码如下:

详解LRU缓存算法[通俗易懂]

详解LRU缓存算法[通俗易懂]

详解LRU缓存算法[通俗易懂]

详解LRU缓存算法[通俗易懂]

详解LRU缓存算法[通俗易懂]

每个方法和成员变量前都有中文注释,不必过多解释。

值得一提的是,Java API中其实已经有数据类型提供了我们需要的功能,就是LinkedHashMap这个类。该类内部也是采用HashMap+双向链表实现的。使用这个类实现LRU就简练多了。

详解LRU缓存算法[通俗易懂]

详解LRU缓存算法[通俗易懂]

只需要覆写LinkedHashMap的removeEldestEntry方法,在缓存已满的情况下返回true,内部就会自动删除最老的元素。

感兴趣的同学可以做一下LeetCode 146. LRU Cache这道题,尝试一下如何实现这个算法。

详解LRU缓存算法[通俗易懂]

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/111943.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 光纤交换机常用命令

    光纤交换机常用命令使用电脑连接管理网口,默认IP地址为:192.168.0.1,掩码:255.255.255.0默认用户名:admin,默认密码password1.switchStatusShow查看交换机的总体健康状态switch:admin>switchstatusshowSwitchHealthReportSwitchName:SWF…

    2022年5月11日
    96
  • NET Framework 类库 OleDbCommand 成员

    NET Framework 类库 OleDbCommand 成员公共构造函数OleDbCommand构造函数已重载。初始化OleDbCommand类的新实例。公共属性CommandText获取或设置要对数据源执行的SQL

    2022年5月19日
    41
  • 控制流和数据流的区别_jdk动态代理底层原理

    控制流和数据流的区别_jdk动态代理底层原理JBPM工作流原理

    2025年10月1日
    4
  • c语言strsep,C/C++ 字符串分割: strtok 与 strsep 函数说明(示例代码)

    c语言strsep,C/C++ 字符串分割: strtok 与 strsep 函数说明(示例代码)函数原型:char*strtok(char*s,constchar*delim);char*strsep(char**s,constchar*delim);功能:strtok和strsep两个函数的功能都是用来分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。返回值:从s开头开始的一个个子串,当没有分割的子串时返回NULL。相同点:两者都会改变源字符串,想要…

    2025年7月23日
    5
  • MATLAB矩阵复制及扩充

    MATLAB矩阵复制及扩充文章目录一、repmat()方法二、Kron()方法一、repmat()方法例一:(行方向和列方向分别平铺矩阵,红框2,3表示平铺倍数)TIPS:【原矩阵】处可用用户自定义的矩阵变量代替   即Mat=[123;456],可用Mat替代function中原矩阵的位置例二:(行方向和列方向分别平铺矩阵,红框2,3表示平铺倍数)例三:(列方向平铺矩阵,红框2表示平铺矩阵的倍数)例四:(行方向平铺矩阵,红框3表示平铺矩阵的倍数)二、Kron()方法kron(A,B)

    2022年6月25日
    170
  • 焦点科技怎么老是招人_现在的白内障手术和过去有何不同?晶体该怎么选?(小刀视频)…

    焦点科技怎么老是招人_现在的白内障手术和过去有何不同?晶体该怎么选?(小刀视频)…■文|赵小刀我是赵小刀,我是一名眼科医生。很多人可能在10年前或者20年前就做过白内障手术了,可能在当时也花了几千块钱,不算小数目。那过去的白内障手术和现在白内障手术有什么不同?人工晶体的种类有哪些,又该怎么选呢?请看节目。请直接点击视频https://www.zhihu.com/video/1201468244097499136白内障手术真的进步了20年前的白内障手术,其实对医生来说非常简单——…

    2022年6月6日
    57

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号