class LRUCache {
// 双向链表节点类,包含 key、value 和双向指针
class DLinkedNode {
int key; // 节点的key
int value; // 节点的value
DLinkedNode prev; // 双向链表的前驱指针
DLinkedNode next; // 双向链表的后继指针
public DLinkedNode() {
};
public DLinkedNode(int key, int value) {
this.key = key;
this.value = value;
}
}
// 缓存,使用Map来快速查找节点
Map<Integer, DLinkedNode> cache;
// 容量,表示最大缓存量
int capacity;
// 当前缓存的大小
int size;
// 虚拟头节点,简化双向链表的操作
DLinkedNode head;
// 虚拟尾节点,简化双向链表的操作
DLinkedNode tail;
// LRUCache的构造函数,初始化缓存大小和双向链表
public LRUCache(int capacity) {
cache = new HashMap<>(); // 初始化缓存,使用哈希表来存储键值对
this.capacity = capacity; // 设置缓存的最大容量
size = 0; // 当前缓存的大小
// 初始化双向链表的虚拟头尾节点
head = new DLinkedNode();
tail = new DLinkedNode();
// 虚拟头尾节点互相连接
head.next = tail;
tail.prev = head;
}
// 获取缓存中的某个元素,若元素存在则移动到头部并返回其值,若不存在则返回-1
public int get(int key) {
DLinkedNode node = cache.get(key); // 从缓存中获取节点
if(node == null) {
return -1; // 如果缓存中没有该节点,返回-1
}
// 将节点移动到链表的头部
MoveToHead(node);
return node.value; // 返回节点的值
}
// 向缓存中插入元素,若缓存已满,则淘汰最不常使用的元素
public void put(int key, int value) {
DLinkedNode node = cache.get(key); // 获取缓存中是否已有该节点
if(node != null) { // 如果节点已存在
MoveToHead(node); // 移动该节点到链表头部
node.value = value; // 更新节点的值
} else { // 如果节点不存在
DLinkedNode newNode = new DLinkedNode(key, value); // 创建一个新的节点
cache.put(key, newNode); // 将新节点加入缓存
size++; // 增加缓存的大小
addToHead(newNode); // 将新节点添加到链表头部
if(size > capacity) { // 如果缓存大小超过容量
// 删除链表尾部的节点(最不常使用的节点)
DLinkedNode del = removeTail();
cache.remove(del.key); // 从缓存中移除该节点
size--; // 缩小缓存大小
}
}
}
// 1. 添加节点到链表的头部
public void addToHead(DLinkedNode node) {
// 设定节点的 next 和 prev 指针,插入到头部
node.next = head.next;
node.prev = head;
head.next.prev = node; // 让原本头部节点的 prev 指向新节点
head.next = node; // 让头部节点的 next 指向新节点
}
// 2. 删除链表中的某个节点
public void removeOne(DLinkedNode node) {
// 修改前后节点的指针,移除当前节点
node.prev.next = node.next;
node.next.prev = node.prev;
}
// 3. 删除链表的尾部节点
public DLinkedNode removeTail() {
// 获取尾部节点(最不常用的节点)
DLinkedNode res = tail.prev;
removeOne(res); // 从链表中移除该节点
return res; // 返回被移除的节点
}
// 4. 将节点移动到链表头部,表示该节点被访问过
public void MoveToHead(DLinkedNode node) {
removeOne(node); // 移除该节点
addToHead(node); // 将该节点添加到链表头部
}
}
/**
* 使用该LRUCache类的方式示例:
* LRUCache obj = new LRUCache(capacity); // 初始化缓存,指定容量
* int param_1 = obj.get(key); // 获取指定key的值
* obj.put(key,value); // 插入key和value到缓存中
*/