LRU
学过 OS 的都知道,缓存有几个缓存淘汰机制,其中最有名的,就是 LRU 缓存淘汰机制
即在缓存快满的情况下,将最近使用最少的内存空间腾出来
那这个算法具体是怎么实现的呢?下面就让我们来探究一下
一、LRU 工作流程
我们先来看一下,LRU 具体是个什么流程:
假设我们的缓存大小为 3,目前已经存入了三个数 1,2,3
:
[1,2,3]
这时我们使用了2
,那 2 就是最近使用的数,被提到最前面:
[1,3,2]
这时我们想插入一个 4,因为 1 是最近最少用的值,我们将 1 替换掉:
[3,2,4]
然后,我们又要去用 3:
[2,4,3]
…
至此,我们大致能了解 LRU 具体是要实现一个什么东西了
二、数据结构选型
我们可以发现,数据的初入,与队列十分相似,那么数据存储的数据结构我们就选择队列了,并且队列的实现我们使用 双向链表
但是,双向链表的查找时间复杂度为 O(n)
,为了让时间复杂度降为 O(1)
,我们可以将其与哈希表混用,实现一个复合数据结构哈希链表
三、具体实现
Java 中,有封装好的数据结构 LinkedHashMap
,可以用来实现 LRU ,但是直接使用它我们这篇文章就没有什么意义了,我们还是要自己造一回轮子。
上回说到,实现 LRU,我们要使用复合数据结构 哈希链表,我们这里,就先实现双端队列,再去实现哈希链表
- 节点数据结构:
public class DNode {
// 节点中存储 key 值,是为了方便在 哈希链表中,删除 hash 表中的 key 值
public Integer key;
public Integer val;
public DNode next;
public DNode pre;
public DNode(Integer val) {
this.val = val;
}
public DNode(Integer key, Integer val) {
this.key = key;
this.val = val;
}
}
- 双端队列:
public class DoubleList {
private int size=0;
DNode head = null;
DNode tail = null;
public DoubleList() {
}
public void addLast(DNode node) {
if (head==null) {
head=node;
tail=head;
} else {
tail.next=node;
tail.next.pre=tail;
tail=tail.next;
}
++size;
}
public DNode removeFirst() {
if (size<=0) return null;
DNode tmp = head;
if (head.next==null) {
head=null;
tail=null;
} else {
head.next.pre=null;
head=head.next;
tmp.next=null;
}
-- size