问题:使用Java完成一个简单的LRU算法
什么是LRU算法
LRU(Least Recently Used),也就是最近最少使用。一种有限的空间资源管理的解决方案,会在空间资源不足的情况下移除掉最近没被使用过的数据,以保证接下来需要的空间资源。
在现在通用的操作系统中为了解决内存不足这个问题,提出了虚拟内存这种解决方案,其实虚拟内存也就是将机器的内存分为多个页面(提个小问题,一个页面包含了多少kb的空间?),内存中只存放当前需要的页面信息,暂时不使用的内存数据就保存到磁盘中。这可以很好的解决内存不足的问题。当然了这就无故出现页面交换的情况,使得读取内存的速度降低(磁盘的读取速度远小于内存的读取速度),这种方案肯定有利有弊,只需要我们的服务能够接受这种情况,那就完全没有问题。
Redis做为一种内存数据库,内存大小对数据库的影响更重要,所以redis也需要及时的移除掉那些过期数据。在redis中有定时清楚、惰性删除、定期删除,但是其策略主要分为两种,基于访问时间和基于访问频率。基于访问时间就是LRU算法,看看LRU算法的图解过程,如下图。
image.png
先定义好一个定长的队列
按照FIFO的流程,依次申请一段空间
直到队列被占满了,出现内存不足的情况,淘汰策略开始工作
会淘汰队列中最先进入的数据,最先进去的数据也就是最近最久未被使用的数据,然后把其移除出队列
LRU 算法小demo
整体的算法实现没有太多的难度,维护一个有限长度的队列的进出,需要移除或者插入数据。时间复杂度可能会是个问题。
队列如果是链表,则移除数据的时间复杂度是O(1),但是查找数据的时间复杂度是O(n)
队列如果是数组,则移除数据的时间复杂度是O(n),而且移除数据还伴随着数组的平行移动,查找数据也是O(n),除非另外再加一个Map存储其索引值会使得其查找的速度降低到O(1),但是却又提高了空间复杂度
接下来写个基于数组的LRU的简单代码
public class LruDemo {
private Object[] items;
private HashMap map;
private int size;
private int index;
public LruDemo() {
this(8);
}
public LruDemo(int size) {
this.size = size;
this.items = new Object[size];
this.map = new HashMap<>(16);
this.index = 0;
}
public void put(T t) {
Integer value = map.get(t);
if (value == null) {
if (index >= size) {
// 满了,需要移除第一个元素
for(int i=1; i
items[i-1] = items[i];
map.replace((T)items[i-1], i);
}
ind