LRU(Least Recently Used:最近最少使用):简单的说,就是保证基本的 Cache容量,如果超过容量则必须丢掉最不常用的缓存数据,再添加最新的缓存。每次读取缓存都会改变缓存的使用时间,将缓存的存在时间重新刷新。其实,就是清理缓冲的一种策略。
我们可以通过双向链表的数据结构实现 LRU Cache,链表头(head)保存最新获取和存储的数据值,链表尾(tail)既为最不常使用的值,当需要清理时,清理链表的 tail 即可,并将前一个元素设置为tail。结构图如下:
**Java 代码实现:**1)、通过原理的分析,我们可以维护一个双向链表结构,如下:LRUNode 实体类,主要理解 prev 和 next 属性,结合上面的链表结构图,就可以理解为,当此对象为 3 时 , prev 应指向 2 , next 应指向 4 。此类为下面类的类种类。
1 /**
2 * @ClassName: LRUNode
3 * @Description: 定义一个链表类,包含head(指针的上一个对象),tail(指针的下一个对象)类种类
4 * @author: zzx
5 * @date: 2019年3月4日 下午10:32:58
6 */
7 class LRUNode {
8 private String key;
9 private Object value;
10 //当前对象的上一个对象
11 LRUNode prev;
12 //当前对象的下一个对象
13 LRUNode next;
14
15 /**
16 * @param key value
17 * @desc 带有参数的构造器
18 */
19 public LRUNode(String key,Object value) {
20 this.key=key;
21 this.value=value;
22 }
23 }
2)、我们通过 HashMap 实现一个缓存类 LRUCache *,*我们通过逻辑处理,对最少使用的数据进行删除。代码如下:
1 /**
2 * @ClassName: LRUCache
3 * @Description: 实现 LRU策略 清除缓存(当缓存大小>=指定大小时)
4 * @author: zzx
5 * @date: 2019年3月4日 下午11:42:11
6 */
7 public class LRUCache {
8 private int capacity;
9 /**
10 * @desc 用来存储 key 和 LRUNode对象 充当缓存,且 hashmap 中的数据不会自动清理,需要我们手动清理
11 */
12 private HashMap<String, LRUNode> hashMap;
13
14 //数据链表 第一个对象
15 private LRUNode head;
16 //数据链表 最后一个对象
17 private LRUNode tail;
18
19 //带参数 capacity 缓存打下的构造器
20 public LRUCache(int capacity) {
21 this.capacity=capacity;
22 //创建对象时,创建一个hashmap 充当缓存
23 hashMap = new HashMap<String,LRUNode>();
24 }
25 /**
26 * @Description: 向链表中插入数据
27 * @return: void
28 */
29 public void set(String key , Object value) {
30 //通过 key 查询链表中是否存在此对象
31 LRUNode lruNode = hashMap.get(key);
32 //缓存中有值时
33 if(lruNode != null) {
34 //为key 附新值,替换就值
35 lruNode.value = value;
36 //更新链表指针
37 appendTail(lruNode,false);
38 //缓存中无值时
39 }else {
40 //新对象
41 lruNode = new LRUNode(key, value);
42 //判断 hashMap 是否大于缓存大小
43 if(hashMap.size() >= capacity) {
44 appendTail(tail,true);
45 }
46 //存取新值
47 hashMap.put(key, lruNode);
48 }
49
50 //将新值设置为 head
51 setHead(lruNode);
52 }
53
54 /**
55 * @Title: appendTail
56 * @Description: 更新链表的前后指针,新增数据公用
57 */
58 public void appendTail(LRUNode lNode,boolean flag) {
59 //存在上一个对象 prev
60 if(lNode.prev != null) {
61 //当前对象的上一个对象指向,当前对象的下一个对象。例如:2<-3(当前对象)->4 更新后: 2——>4
62 lNode.prev.next = lNode.next;
63 //如果不存在,下一个对象为 head 对象(暂不考虑更改对象)
64 }else {
65 head=lNode.next;
66 }
67
68 //存在下一个对象 next
69 if(lNode.next != null) {
70 // 4——>2
71 lNode.next.prev=lNode.prev;
72 }else {
73 //如果当前对象是最后一个对象,则上一个对象就为最后一个对象
74 tail=lNode.prev;
75 }
76
77 lNode.prev=null;
78 lNode.next=null;
79 //如果内存不足,需要删除tail
80 if(flag) {
81 hashMap.remove(lNode.key);
82 }
83 }
84
85 /**
86 * @Title: setHead
87 * @Description: 设置链表的 头 节点
88 * @param: node
89 * @return: void
90 */
91 public void setHead(LRUNode node) {
92 if(head != null) {
93 node.next=head;
94 head.prev=node;
95 }
96 head=node;
97 if(tail == null) {
98 tail=node;
99 }
100 }
101 }
d.prev=node;
95 }
96 head=node;
97 if(tail == null) {
98 tail=node;
99 }
100 }
101 }