简介

LRU(Least Recently Used)直译为“最近最少使用”。其实很多老外发明的词直译过来对于我们来说并不是特别好理解,甚至有些词并不在国人的思维模式之内,比如快速排序中的Pivot,模拟信号中的Analog 等等。笔者认为最好的理解方式就是看他诞生的原因,看这个概念的出现如何一步一步演变为现在的样子。假如说你自己对某个问题想到了一个解决办法,于是你按照语义给他起了个名字,假如你直接将这个词儿说给别人,不知道这个词儿来历的人大概很难理解。所以为了力求方便理解,下面我们先来看看LRU是什么,主要是为了解决什么问题。

其实LRU这个概念映射到现实生活中非常好理解,就好比说小明的衣柜中有很多衣服,假设他的衣服都只能放在这个柜子里,小明每过一阵子小明就会买新衣服,不久小明的衣柜就放满了衣服。这个小明想了个办法,按照衣服上次穿的时间排序,丢掉最长时间没有穿过那个。这就是LRU策略。

映射到计算机概念中,上述例子中小明的衣柜就是内存,而小明的衣服就是缓存数据。我们的内存是有限的。所以当缓存数据在内存越来越多,以至于无法存放即将到来的新缓存数据时,就必须扔掉最不常用的缓存数据。所以对于LRU的抽象总结如下:

  • 缓存的容量是有限的
  • 当缓存容量不足以存放需要缓存的新数据时,必须丢掉最不常用的缓存数据

实现

理解了LRU的原理之后,想要将其转换为代码并不是一件很困难的事。我们访问缓存通常使用一个字符串来定位缓存数据(事实上其他数据形式也没有关系),这种场景我们反射性的想到HashMap。所以我们先来简单的定义一下LRUCachce类。

public class LRUCache {private HashMap<String, Object> map;private int capacity;public Object get(String key) {return map.get(key);}public void set(String key, Object value) {this.map.put(key, value);}public LRUCache(int capacity) {this.capacity = capacity;this.map = new HashMap<String, Object>();}
}

这样我们仅仅是定义了一个容量有限的LRUCache,可以存取数据,但并没有实现缓存容量不足时丢弃最不常用的数据的功能,而这件事做起来似乎显得稍微麻烦一些,问题在于我们如何找到最久没有用的缓存。

一个最容易想到的办法是我们在给这个缓存数据加一个时间戳,每次get缓存时就更新时间戳,这样找到最久没有用的缓存数据问题就能够解决,但与之而来的会有两个新问题:

  • 虽然使用时间戳可以找到最久没用的数据,但我们最少的代价也要将这些缓存数据遍历一遍,除非我们维持一个按照时间戳排好序的SortedList。
  • 添加时间戳的方式为我们的数据带来了麻烦,我们并不太好在缓存数据中添加时间戳的标识,这可能需要引入新的包含时间戳的包装对象。

而且我们的需要只是找到最久没用使用的缓存数据,并不需要精确的时间。添加时间戳的方式显然没有利用这一特性,这就使得这个办法从逻辑上来讲可能不是最好的。

然而办法总是有的,我们可以维护一个链表,当数据每一次查询就将数据放到链表的head,当有新数据添加时也放到head上。这样链表的tail就是最久没用使用的缓存数据,每次容量不足的时候就可以删除tail,并将前一个元素设置为tail,显然这是一个双向链表结构,因此我们定义LRUNode如下:

class LRUNode {String key;Object value;LRUNode prev;LRUNode next;public LRUNode(String key, Object value) {this.key = key;this.value = value;}
}

而LRUCache的简单实现最终如下:

public class LRUCache {private HashMap<String, LRUNode> map;private int capacity;private LRUNode head;private LRUNode tail;public void set(String key, Object value) {LRUNode node = map.get(key);if (node != null) {node = map.get(key);node.value = value;remove(node, false);} else {node = new LRUNode(key, value);if (map.size() >= capacity) {// 每次容量不足时先删除最久未使用的元素remove(tail, true);}map.put(key, node);}// 将刚添加的元素设置为headsetHead(node);}public Object get(String key) {LRUNode node = map.get(key);if (node != null) {// 将刚操作的元素放到headremove(node, false);setHead(node);return node.value;}return null;}private void setHead(LRUNode node) {// 先从链表中删除该元素if (head != null) {node.next = head;head.prev = node;}head = node;if (tail == null) {tail = node;}}// 从链表中删除此Node,此时要注意该Node是head或者是tail的情形private void remove(LRUNode node, boolean flag) {if (node.prev != null) {node.prev.next = node.next;} else {head = node.next;}if (node.next != null) {node.next.prev = node.prev;} else {tail = node.prev;}node.next = null;node.prev = null;if (flag) {map.remove(node.key);}}public LRUCache(int capacity) {this.capacity = capacity;this.map = new HashMap<String, LRUNode>();}
}

转载于:https://www.cnblogs.com/nicky-160330/p/9498481.html

LRU的理解与Java实现相关推荐

  1. 一文带你深入理解【Java基础】· 枚举类

    写在前面 Hello大家好, 我是[麟-小白],一位软件工程专业的学生,喜好计算机知识.希望大家能够一起学习进步呀!本人是一名在读大学生,专业水平有限,如发现错误或不足之处,请多多指正!谢谢大家!!! ...

  2. 一文带你深入理解【Java基础】· Java基本语法:程序流程控制

    写在前面 Hello大家好, 我是[麟-小白],一位软件工程专业的学生,喜好计算机知识.希望大家能够一起学习进步呀!本人是一名在读大学生,专业水平有限,如发现错误或不足之处,请多多指正!谢谢大家!!! ...

  3. 第二季:7.怎么查看服务器默认的垃圾收集器是那个?生产上如何配置垃圾收集器的?谈谈你对垃圾收集器的理解?【Java面试题】

    第二季:7.怎么查看服务器默认的垃圾收集器是那个?生产上如何配置垃圾收集器的?谈谈你对垃圾收集器的理解?[Java面试题] 前言 推荐 7.怎么查看服务器默认的垃圾收集器是那个?生产上如何配置垃圾收集 ...

  4. 深入理解Android Java虚拟机ART相关及勘误

    公众号: 神农和朋友们的杂文集 目录 本书的简单介绍 本书各章的难度评估 深入学习有什么好处? 本书勘误表 P14 P21 本书的简单介绍 <深入理解Android Java虚拟机ART> ...

  5. 谈谈你对java的理解,java是“解释执行”这句话对吗?

    谈谈你对java的理解,java是"解释执行"这句话对吗? 先科普下什么是"解释执行"? 什么是"编译执行"? • 解释执行 解释执行时高级 ...

  6. 一文带你深入理解【Java基础】· 面向对象编程(下)②代码块、final和abstract

    写在前面 Hello大家好, 我是[麟-小白],一位软件工程专业的学生,喜好计算机知识.希望大家能够一起学习进步呀!本人是一名在读大学生,专业水平有限,如发现错误或不足之处,请多多指正!谢谢大家!!! ...

  7. 一文带你深入理解【Java基础】· 数组

    写在前面 Hello大家好, 我是[麟-小白],一位软件工程专业的学生,喜好计算机知识.希望大家能够一起学习进步呀!本人是一名在读大学生,专业水平有限,如发现错误或不足之处,请多多指正!谢谢大家!!! ...

  8. 好理解的Java内存虚假共享(False Sharing)性能损耗以及解决方案

    虚假共享(False Sharing)也有人翻译为伪共享 参考 https://en.wikipedia.org/wiki/False_sharing 在计算机科学中,虚假共享是一种性能降低的使用模式 ...

  9. 安卓 java内存碎片_理解Android Java垃圾回收机制

    Jvm(Java虚拟机)内存模型 从Jvm内存模型中入手对于理解GC会有很大的帮助,不过这里只需要了解一个大概,说多了反而混淆视线. Jvm(Java虚拟机)主要管理两种类型内存:堆和非堆. 堆是运行 ...

最新文章

  1. 仲兆鹏 160809329 第5次
  2. 一毕业年薪 201 万!目前全球仅四人,华为最高档“天才少年”这样说
  3. foreach遍历----for(object o: list)
  4. 32位mysql安装包_《MySQL 入门教程》第 02 篇 MySQL 安装
  5. WindowsPowerShell常用命令
  6. 库函数、系统调用和内核函数的区别
  7. tp5 cache缓存简单使用
  8. 好玩的php函数,几个实用的PHP函数
  9. QT学习笔记:常用快捷键总结表
  10. Pl/SQL Developer 配置
  11. 《大话设计模式》——外观模式
  12. SEO基础问题:14.给图片添加alt标签的知识点
  13. [附源码]Java计算机毕业设计SSM大众点评管理系统
  14. 小米手机便签怎么批量导出到另一个手机
  15. resin服务器搭建
  16. 计算机cpu的字母,笔记本计算机CPU型号后缀字母HQ,U,Y的详细说明
  17. 15.计算几何: 坐标值的精度【eps、sgn()、dcmp()】+ 平面上的点用struct表示 + 向量的定义与加减乘除
  18. 服务器地址为空,dns服务器地址为空
  19. 使用torchvision.models
  20. 杰奇小说系统百度地图生成插件

热门文章

  1. 工业控制软件测试评价体系,工业控制信息安全产品测试评价的体系.doc
  2. 中业科技机器人价格_协作机器人售价持续走低 本土厂商该如何发力
  3. python lol脚本_配台电脑,能玩LOL顶配和能够写一些python脚本能用ps不卡,预算6k到8k?...
  4. genrsa out php,PHP进行RSA加密解密
  5. esp32 micropython spiffs_二十一,ESP32 SPIFFS:读取一个文件
  6. 你不知道的三种在for循环中使用setTimeout的方法
  7. vs添加码云远程_项目git步骤,将项目托管到码云。git本地分支推送到远程分支...
  8. 千兆网卡为什么慢_千兆宽带普及率不到1% 消费者为何不愿意用千兆宽带?
  9. android VectorDrawable使用笔记(三)
  10. Tor真的匿名和安全吗?——如果是http数据,则在出口节点容易被嗅探明文流量,这就是根本问题...