目录
- 1、问题
- 2、答案
- 3、结果
- 4、解释
1、问题
2、答案
package com.atguigu.jmap;
import java.util.HashMap;
import java.util.Map;
// 哈希表 + 双向链表(哈希表中的key是int类型的值,value是Node节点,双向链表中存储的就是Node节点)
class LRUCache {
// 存储容量
private int capacity;
// 存储Node节点的Map集合
private Map<Integer, Node> map = new HashMap<>();
// 存储Node节点的双向链表
private DoubleLinkedList list = new DoubleLinkedList();
public LRUCache(int capacity) {
this.capacity = capacity;
}
// 如果存在返回对应的值,否则返回-1
public int get(int key) {
Node node = map.get(key);
if (node != null) {
// 将该节点移动到双向链表的表头
list.moveToHead(node);
return node.value;
}
return -1;
}
// 添加值
public void put(int key, int value) {
// 获取节点
Node node = map.get(key);
// 如果节点存在,直接更新节点的值
if (node != null) {
node.value = value;
// 将该节点移动到双向链表的表头
list.moveToHead(node);
return;
}
// 如果容量已经满了,那就要删除末尾的节点
if (map.size() == capacity) {
// 获取尾部节点之前的节点,这个节点是我们自己的
Node endNode = list.getEndNode();
// 删除map集合中的该节点
map.remove(endNode.key);
// 删除双向链表中的该节点
list.removeNode(endNode);
}
// 新建一个节点
Node newNode = new Node(key, value);
// 将该节点放在map中
map.put(key, newNode);
// 将该节点放在双向链表的表头
list.addNode(newNode);
}
// 双向链表中的节点
class Node {
int key;
int value;
// 前一个节点
Node prev;
// 后一个节点
Node next;
Node() {
}
Node(int key, int value) {
this.key = key;
this.value = value;
}
}
// 双向链表
class DoubleLinkedList {
// 双向链表的头结点和尾节点
Node head, tail;
// 必须有这样一个操作,因为我们下面需要用到head和tail
DoubleLinkedList() {
head = new Node();
tail = new Node();
head.next = tail;
tail.prev = head;
}
// 添加节点
void addNode(Node node) {
// 先对付新节点
node.prev = head;
node.next = head.next;
// 先更新node后面的节点
head.next.prev = node;
// 这个必须放在最后,因为前面都需要用到
head.next = node;
}
// 返回尾部节点之前的节点
Node getEndNode() {
return tail.prev;
}
// 删除某节点
void removeNode(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
// 最后清除该节点和其他节点的联系,用来方便垃圾回收
node.prev = null;
node.next = null;
}
// 将某节点移动到头部
void moveToHead(Node node) {
this.removeNode(node);
this.addNode(node);
}
}
public static void main(String[] args) {
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
System.out.println(lRUCache.get(1)); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
System.out.println(lRUCache.get(2)); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
System.out.println(lRUCache.get(1)); // 返回 -1 (未找到)
System.out.println(lRUCache.get(3)); // 返回 3
System.out.println(lRUCache.get(4)); // 返回 4
}
}
3、结果
1
-1
-1
3
4
4、解释
这道题其实是力扣上的题,我只是把我的解法分享一下,题目地址是https://leetcode-cn.com/problems/lru-cache,这道题想考察的知识点其实和redis有关,由于redis中有可能出现OOM的情况,如果要出现这种情况,我们就需要去有应对之法,默认采用不处理的策略,即:
也就是任凭内存溢出,不会进行处理;
但是这种策略不能用在生产上,所以我们可以使用另外一种处理方式,也就是allkeys-lru,可以在redis的配置文件中进行设置,具体设置方法如下:
该策略在配置文件中也有解释,如下:
含义是:“使用近似LRU算法逐出任意key”,其中提到了LRU算法,我们来看一下配置文件中对LRU算法的解释,如下:
含义是:“LRU算法意味着最近使用过的”,也就是删除那些最不常用的,然后LRU算法的具体处理方式在上面问题中已经说了很清晰了,这里不再赘述;
然后问题的文字描述我已经在代码中说得很清楚了,这里再用图来说明一下;
大体结构如下:
细分结构: