LRU(Least Recently Used)是一种常见的缓存淘汰策略,用于在缓存容量不足时确定要淘汰的缓存项。LRU策略基于一种简单的思想:最近最少使用的项最先被淘汰。
1.当一个数据被访问时,它就被标记为最近使用过的。
2.当缓存满了且需要淘汰一个数据项时,LRU算法会选择最近最少使用的数据项进行淘汰。
下面我们采用双向链表 + 哈希表的方式手写一个LRU算法。
// Hash表,用于快速查找节点
private Map<Integer,Node> cache;
// 缓存容量,当超过容量需要淘汰
private int capacity;
// 链表头节点,标记最近访问的节点
private Node head;
// 链表尾节点,标记最旧的节点
private Node tail;
// Node节点信息
class Node{
int key;
int value;
Node pre;
Node next;
}
public LRUCache(int capacity) {
// 创建hash表
this.cache = new HashMap<>();
// 设置容量
this.capacity = capacity;
// 构建双向链表
head.next = tail;
tail.pre = head;
}
// 移除链表中的节点
private void removeNode(Node node){
Node pre = node.pre;
Node next = node.next;
pre.next = next;
next.pre = pre;
}
private void addToFront(Node node){
Node next = head.next;
head.next = node;
node.pre = head;
node.next = next;
next.pre = node;
}
// get获取元素
public int get(int key){
if(cache.containsKey(key)){
Node node = cache.get(key);
// 从链表中删除该节点
removeNode(node);
// 该节点放到链表头部(最近访问)
addToFront(node);
return node.value;
}
return -1;
}
// add增加元素
public void put(int key,int value){
// 旧key,替换value,该节点放到链表头部
if(cache.containsKey(key)){
Node node = cache.get(key);
node.value = value;
removeNode(node);
addToFront(node);
return ;
}
// 新key,构建Node
Node newNode = new Node(key, value);
cache.put(key, newNode);
// 超过阈值,删除尾节点
if(cache.size() > capacity){
Node toRemove = tail.pre;
removeNode(toRemove);
cache.remove(toRemove.key);
}
// 节点放到链表头部
addToFront(newNode);
}
public class LRUCache {
// 用于快速查找节点的HashMap
private Map<Integer,Node> cache;
// 缓存容量
private int capacity;
// 链表头节点,标记最近访问的节点
private Node head;
// 链表尾节点,标记最旧的节点
private Node tail;
// 初始化LRUCache
public LRUCache(int capacity) {
this.cache = new HashMap<>();
this.capacity = capacity;
head.next = tail;
tail.pre = head;
}
// 将节点添加到链表头部
private void addToFront(Node node){
Node next = head.next;
head.next = node;
node.pre = head;
node.next = next;
next.pre = node;
}
// 移除链表中的节点
private void removeNode(Node node){
Node pre = node.pre;
Node next = node.next;
pre.next = next;
next.pre = pre;
}
// 获取节点值并将节点移到最前面表示最近访问
public int get(int key){
if(cache.containsKey(key)){
Node node = cache.get(key);
removeNode(node);
addToFront(node);
return node.value;
}
return -1;
}
// 向缓存中添加节点
public void put(int key,int value){
if(cache.containsKey(key)){
Node node = cache.get(key);
node.value = value;
removeNode(node);
addToFront(node);
return ;
}
Node newNode = new Node(key, value);
cache.put(key, newNode);
if(cache.size() > capacity){
Node toRemove = tail.pre;
removeNode(toRemove);
cache.remove(toRemove.key);
}
addToFront(newNode);
}
/**
* 内部节点类
*/
@Data
class Node{
int key;
int value;
Node pre;
Node next;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
}