算法必学之LRU
核心:最近最少使用的踢出缓存
实现:一个双向链表,越靠近尾部,最近越少使用,链表节点存储真正的数据key-value。数据集存储在HashMap中,标注头尾节点。
package org.example.test01;
import java.util.HashMap;
import java.util.Map;
public class LRUCache {
// 这里的键,值,用int型为例
// 双向链表节点,真正数据key-value所在
class DLinkedNode {
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
public DLinkedNode() {
}
public DLinkedNode(int key, int value) {
this.key = key;
this.value = value;
}
}
// 真正缓存的数据集,存储key-双向链表节点
private Map<Integer, DLinkedNode> cache = new HashMap<Integer, DLinkedNode>();
// 当前缓存大小
private int size;
// 缓存容量
private int capacity;
// 头尾指针,方便我们操作
private DLinkedNode head, tail;
// 初始化LRU缓存
public LRUCache(int capacity) {
this.size = 0;
this.capacity = capacity;
head = new DLinkedNode();
tail = new DLinkedNode();
head.next = tail;
tail.prev = head;
}
// 判断缓存中是否有该key,
// 没有就get不到,返回-1
// 有就刷新最新最近使用,也就是将对应数据的链表节点移到头部
public int get(int key) {
DLinkedNode node = cache.get(key);
if (node == null) {
return -1;
}
moveToHead(node);
return node.value;
}
// 判断缓存中是否有该key,
// 没有就put一个,也就是创建新的链表节点,放入缓冲区中,设置好对应头结点,
// 但若超过容量需要删除尾部节点
// 有就直接修改对应值,并更新最新最近使用数据
public void put(int key, int value) {
DLinkedNode node = cache.get(key);
if (node == null) {
DLinkedNode newNode = new DLinkedNode(key, value);
cache.put(key, newNode);
addToHead(newNode);
++size;
if (size > capacity) {
// 如果超出容量,删除双向链表的尾部节点
DLinkedNode tail = removeTail();
// 删除哈希表中对应的项
cache.remove(tail.key);
--size;
}
} else {
node.value = value;
moveToHead(node);
}
}
private DLinkedNode removeTail() {
DLinkedNode res = tail.prev;
removeNode(res);
return res;
}
// 这里是将该节点相应位置删除,再插入头部,达到移动数据(更新最近使用)的数据的效果
private void moveToHead(DLinkedNode node) {
removeNode(node);
addToHead(node);
}
// 头部添加节点,需要头部节点跟当前节点建立联系,
// 但在此之前需要当前节点尾部跟原来的数据建立联系,
// 这样头部就放心跟当前节点建立联系。
private void addToHead(DLinkedNode node) {
// 当前节点尾部跟原来的数据建立联系
node.next = head.next;
head.next.prev = node;
// 头部放心跟当前节点建立联系
head.next = node;
node.prev = head;
}
// 删除只需要当前节点前一个后一个节点建立联系就行
private void removeNode(DLinkedNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
}