一、简介
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。他是redis的一种常用淘汰策略,只是Redis中的LRU与常规的LRU实现并不相同,常规LRU会准确的淘汰掉队头的元素,但是Redis的LRU并不维护队列,只是根据配置的策略要么从所有的key中随机选择N个(N可以配置)要么从所有的设置了过期时间的key中选出N个键,然后再从这N个键中选出最久没有使用的一个key进行淘汰。
二、利用Java来手写LRU算法
1.通过LRU2的构造方法指定容量,通过put和get来存放、查询数据
package com.maoyan.luzelong.MathLogic;
import java.util.HashMap;
import java.util.Map;
public class LRU2 {
class Node<k,v> {
k key;
v value;
Node<k,v> prev;
Node<k,v> next;
public Node(){
this.prev = this.next = null;
}
public Node(k key,v value){
this.key = key;
this.value =value;
this.prev = this.next = null;
}
}
class DoubleLinkedList<k,v> {
Node<k,v> head;
Node<k,v> tail;
//2.1 构造方法
public DoubleLinkedList(){
head = new Node<>();
tail = new Node<>();
head.next = tail;
tail.prev = head;
}
//2.2 添加到头
public void addHead(Node<k,v> node){
node.next = head.next;
node.prev = head;
head.next.prev = node;
head.next = node;
}
//2.3 删除节点
public void removeNode(Node<k,v> node){
node.next.prev = node.prev;
node.prev.next = node.next;
node.prev = null;
node.next = null;
}
//2.4 获取最后一个节点
public Node getLast(){
return tail.prev;
}
}
private int cacheSize;
Map<Integer,Node<Integer,Integer>> map;
DoubleLinkedList<Integer,Integer> doubleLinkedList;
public LRU2(int cacheSize) {
this.cacheSize = cacheSize;//坑位
map = new HashMap<>();//查找
doubleLinkedList = new DoubleLinkedList<>();
}
public int get(int key) {
if (!map.containsKey(key)){
return -1;
}
Node<Integer,Integer> node = map.get(key);
doubleLinkedList.removeNode(node);
doubleLinkedList.addHead(node);
return node.value;
}
public void put(int key,int value){
if (map.containsKey(key)){
Node<Integer, Integer> node = map.get(key);
node.value = value;
map.put(key,node);
doubleLinkedList.removeNode(node);
doubleLinkedList.addHead(node);
}else {
if (map.size() == cacheSize){
Node<Integer,Integer> lastNode = doubleLinkedList.getLast();
map.remove(lastNode.key);
doubleLinkedList.removeNode(lastNode);
}
//新增
Node<Integer, Integer> newNode = new Node<>();
map.put(key,newNode);
doubleLinkedList.addHead(newNode);
}
}
}
2.除了上面的写法以外,java还有个投机取巧的方法,那就是利用LinkedHashMap,我们看jdk的源码会发现里面有下面的描述:
该map很适合构建LRU缓存。
package com.maoyan.luzelong.MathLogic;
import java.util.LinkedHashMap;
import java.util.Map;
public class LRU1<k,v> extends LinkedHashMap<k,v> {
private int capacity ; //缓存坑位
public LRU1(int capacity) {
super(capacity,0.75f,true);
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<k,v> eldest){
return super.size() > capacity;
}
public static void main(String[] args) {
LRU1 lru1 = new LRU1(3);
lru1.put(1,"A");
lru1.put(2,"B");
lru1.put(3,"C");
System.out.println(lru1.keySet());
lru1.put(4,"D");
System.out.println(lru1.keySet());
}
}