Redis中的LRU
Redis被当做缓存来使用,当你新增数据时,让它自动地回收旧数据是件很方便的事情。LRU 实际上是被唯一支持的数据移除方法。
实际中,redis并没有严格遵循LRU的思路去回收旧的数据,具体的redis关于LRU的实现,可以参考这篇文章https://www.cnblogs.com/houziwty/p/5129946.html
本次我们主要使用java实现LRU算法
LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
实现思路:
对一个Cache的操作无非三种:插入(insert)、替换(replace)、查找(lookup)。
为了能够快速删除最久没有访问的数据项和插入最新的数据项,我们使用 双向链表 连接Cache中的数据项,并且保证链表维持数据项从最近访问到最旧访问的顺序。
- 插入:当Cache未满时,新的数据项只需插到双链表头部即可。时间复杂度为O(1)O(1).
- 替换:当Cache已满时,将新的数据项插到双链表头部,并删除双链表的尾结点即可。时间复杂度为O(1)O(1).
- 查找:每次数据项被查询到时,都将此数据项移动到链表头部。
经过分析,我们知道使用双向链表可以保证插入和替换的时间复杂度是O(1)O(1),但查询的时间复杂度是O(n)O(n),因为需要对双链表进行遍历。为了让查找效率也达到O(1)O(1),很自然的会想到使用 hash table 。
代码实现:
1.定义一个Node节点
public class Node {
int key;
int value;
Node pre;
Node next;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
2.LRUCache.java
public class LRUCache {
/**
* 缓存大小
*/
private int capacity;
HashMap<Integer, Node> map = new HashMap<>();
/**
* 头结点
*/
private Node head = null;
/**
* 尾节点
*/
private Node end = null;
/**
* 初始化缓存大小
*/
public LRUCache(int capacity) {
this.capacity = capacity;
}
/**
* 删除节点
*/
public void remove(Node node) {
//需要注意变更pre,end指针情况
if (node.pre != null) {
node.pre.next = node.next;
} else {
head = node.next;
}
if (node.next != null) {
node.next.pre = node.pre;
} else {
end = node.pre;
}
}
/**
* 设置头节点
*/
public void setHead(Node node) {
node.next = head;
node.pre =null;
if (head != null) {
head.pre = node;
}
head = node;
//判断头尾是否为空
if (end == null) {
end = head;
}
}
/**获取一个缓存数据后,应该把这个数据在当前位置删除掉,然后把它重新添加到头的位置
* @param key
* @return
*/
public int get(int key) {
if (map.containsKey(key)) {
Node node = map.get(key);
remove(node);
setHead(node);
return node.value;
}
return -1;
}
/**s设置指定位置的数据
* @param key
* @param value
*/
public void set(int key, int value) {
//如果该位置有元素,那么就替换数据,将其放入头结点
if (map.containsKey(key)) {
Node old = map.get(key);
old.value = value;
remove(old);
setHead(old);
} else {
//如果是新的节点就要判断集合大小是否满足,并将新节点设置到头结点
Node created = new Node(key, value);
if (map.size()>=capacity) {
map.remove(end.key);
remove(end);
setHead(created);
} else {
setHead(created);
}
map.put(key, created);
}
}
}