基于 LinkedHashMap 如何实现LRU缓存调度算法原理
LRU这个算法就是把最近一次使用时间离现在时间最远的数据删除掉
Redis 内存淘汰机制 :
allkeys-lru(least recently used):当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)
List:每次访问一个元素后把这个元素放在 List一端,这样一来最远使用的元素自然就被放到List的另一端。缓存满了的时候就把那最远使用的元素remove掉
但是List太慢,因为List底层是数组 , 要删掉的数据总是位于List底层数组的第一个位置,删掉之后,后面的数据要向前补位, 所以复杂度是O(n)
Map : 链表结构的 LinkedHashMap
,LinkedHashMap
默认的元素顺序是 put
的顺序,但是如果使用带参数的构造函数,那么 LinkedHashMap
会根据访问顺序来调整内部顺序。 LinkedHashMap的get()方法除了返回元素之外还可以把被访问的元素放到链表的底端,这样一来每次顶端的元素就是remove的元素。
仿照Redis 内存淘汰机制的原理,可以自己利用LinkedHashMap写一个LRU算法。
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author : cpucode
* @date : 2021/5/23
* @time : 16:26
* @github : https://github.com/CPU-Code
* @csdn : https://blog.csdn.net/qq_44226094
*/
public class UrlTest {
public static void main(String[] args) {
LinkedHashMap<String, String> map = createUrlMap();
System.out.println("/*******************使用bb************************/");
map.get("bb");
System.out.println(map);
System.out.println("/*******************使用aa************************/");
map.get("aa");
System.out.println(map);
System.out.println();
System.out.println("/*******************加入ee************************/");
map.put("ee","55");
System.out.println(map);
System.out.println("/*******************加入ff************************/");
map.put("ff","66");
System.out.println(map);
}
static final int MAX_NUM = 5;
public static LinkedHashMap<String, String> createUrlMap() {
LinkedHashMap<String, String> map = null;
/**
* @param MAX_NUM 初始容量
* @param 0.75F 加载因子,一般是 0.75f
* @param true false 基于插入顺序 true 基于访问顺序(get一个元素后,这个元素被加到最后,使用了LRU 最近最少被使用的调度算法)
*/
map = new LinkedHashMap<String, String>(MAX_NUM,0.75F ,true){
/**
* 实现LRU的关键方法,如果map里面的元素个数大于了缓存最大容量,则删除链表的顶端元素
*
* @param var
* @return
*/
@Override
protected boolean removeEldestEntry(Map.Entry<String, String> var){
return this.size() > MAX_NUM;
}
};
map.put("aa", "11");
map.put("bb", "22");
map.put("cc", "33");
map.put("dd", "44");
System.out.println("/******************正常顺序************************/");
System.out.println(map);
System.out.println();
return map;
}
}
结果 :
/******************正常顺序************************/
{aa=11, bb=22, cc=33, dd=44}
/*******************使用bb************************/
{aa=11, cc=33, dd=44, bb=22}
/*******************使用aa************************/
{cc=33, dd=44, bb=22, aa=11}
/*******************加入ee************************/
{cc=33, dd=44, bb=22, aa=11, ee=55}
/*******************加入ff************************/
{dd=44, bb=22, aa=11, ee=55, ff=66}