最近使用最少(LRU)缓存是一种缓存逐出算法,它按使用顺序组织元素。顾名思义,在LRU中,最长时间未使用的元素将从缓存中逐出。
例如,如果我们有一个容量为三项的缓存:
最初,缓存是空的,我们将元素 8
放在缓存中。元素 9
和 6
像以前一样被缓存。但现在,缓存容量已满,要放入下一个元素,我们必须逐出缓存中最近使用最少的元素。
在我们用Java实现LRU缓存之前,最好先了解一下缓存的一些方面:
O(1)
LRU缓存的结构
现在,让我们考虑一个有助于我们设计缓存的问题。
我们如何设计一个可以在固定时间内执行读取、排序(时态排序)和删除元素等操作的数据结构?
似乎要找到这个问题的答案,我们需要深入思考关于LRU缓存及其特性的说法:
- 实际上,LRU缓存是一种 队列 —如果重新访问某个元素,它将进入逐出顺序的末尾
- 此队列将具有特定容量,因为缓存的大小有限。每当引入新元素时,它都会添加到队列的最前面。当逐出发生时,它发生在队列的尾部。
- 命中缓存中的数据必须在固定时间内完成,这在队列中是不可能的!但是,使用Java的
HashMap
数据结构是可能的 - 删除最近使用最少的元素必须在固定时间内完成,这意味着对于队列的实现,我们将使用
DoublyLinkedList
而不是SingleLinkedList
或数组
因此,LRU缓存只是 DoublyLinkedList
和 HashMap
的组合,如下所示:
这样做的目的是将密钥保留在Map上,以便快速访问队列中的数据。
LRU算法
LRU算法非常简单!如果密钥存在于 HashMap
中,则为缓存命中;否则,就是缓存丢失了。
缓存未命中后,我们将执行两个步骤:
1. 在列表前面添加新元素。
2. 在 HashMap
中添加一个新条目并引用列表的标题。
在缓存命中后,我们将执行两个步骤:
1. 删除hit元素并将其添加到列表前面。
2. 使用列表前面的新引用更新 HashMap
。
现在,我们来看看如何在Java中实现LRU缓存!
Java实现
首先,我们将定义缓存接口:
public interface Ca