1、LRU是什么?
LRU是常用的缓存过期算法,向缓存添加数据时,如果缓存已满,则需要删除访问时间最早的那条数据,这种更新缓存的方法就叫做LRU
2、哪些应用场景?
Redis, Memcached 等缓存都可以配置使用该算法
3、如何实现?
双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置。
import java.util.*;
public class Solution {
// 构建双向链表
class DLinkedNode {
int key;
int value;
DLinkedNode pre;
DLinkedNode next;
public DLinkedNode() {};
public DLinkedNode(int _key, int _value) {
key = _key;
value = _value;
}
}
// 设置缓存的属性
private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
private int size;
private int capacity;
private DLinkedNode head, tail;
// 初始化缓存
public Solution(int capacity) {
this.size = 0;
this.capacity = capacity;
this.head = new DLinkedNode();
this.tail = new DLinkedNode();
head.next = tail;
tail.next = head;
}
public int get(int key) {
DLinkedNode node = cache.get(key);
// 不存在,返回-1
if (node == null) {
return -1;
}
// 存在,通过hash表定位到节点,然后将节点移动到头部
moveToHead(node);
return node.value;
}
public void set(int key, int value) {
DLinkedNode node = cache.get(key);
if (node == null) {
// 创建新的节点
DLinkedNode newNode = new DLinkedNode(key, value);
// 放入hash
cache.put(key, newNode);
// 放到头部
addToHead(newNode);
// 判断是否需要删除尾部
size++;
if (size > capacity) {
// 超出容量,删除链表尾部节点
DLinkedNode tail = removeTail();
// 删除hash对应的项
cache.remove(tail.key);
}
} else {
// 通过hash表定位,然后修改value,移动到头部
node.value = value;
moveToHead(node);
}
}
private void moveToHead(DLinkedNode node) {
// 删除指定节点
remove(node);
// 在头部加一个节点
addToHead(node);
}
private void remove(DLinkedNode node) {
node.pre.next = node.next;
node.next.pre = node.pre;
}
private void addToHead(DLinkedNode node) {
node.next = head.next;
node.pre = head;
head.next.pre = node;
head.next = node;
}
private DLinkedNode removeTail() {
DLinkedNode res = tail.pre;
remove(res);
return res;
}
}