// LRU缓存机制可以通过哈希表辅以双向链表实现
// 使用双向链表的理由:方便删除节点,O(1)时间
// 双向链表中存储key的理由:size > capcity 时可以O(1)的时间删除哈希表中的键值对
// O(1)的get和put,用hashmap实现
// 基础还是链表的增删改查,只是这里是双向链表作为hashmap的value
// 链表的中节点的移动和删除修改通过双向链表可以在O(1)的时间内实现
// 每访问一个节点都要将其移动链表头。整个过程分为两步:1.双向链表中删除该节点,2.添加到头部
// 链表中的元素个数超过capcity后需要删除链表尾的节点
class LRUCache {
class DLinkedNode {
int key;
int value;
DLinkedNode prev;
DLinkedNode next;
DLinkedNode() {}
DLinkedNode(int key, int value) {
this.key = key;
this.value = value;
}
}
private int size;
private int capacity;
private DLinkedNode head = new DLinkedNode();
private DLinkedNode tail = new DLinkedNode();
private HashMap<Integer, DLinkedNode> hs = new HashMap<>();
LRUCache(int capacity) {
size = 0;
this.capacity = capacity;
head.next = tail;
tail.prev = head;
}
public int get (int key) {
DLinkedNode node = hs.get(key);
if (node == null) {
return -1;
}
moveToHead(node);
return node.value;
}
public void put(int key, int value) {
DLinkedNode node = hs.get(key);
if (node == null) {
node = new DLinkedNode(key, value);
hs.put(key, node);
addToHead(node);
size++;
if (size > capacity) {
DLinkedNode tail = removeTail();
hs.remove(tail.key);
--size;
}
} else {
node.value = value;
moveToHead(node);
}
}
public void addToHead(DLinkedNode node) { // 添加到头结点,意味着新建了一个节点
node.next = head.next;
node.prev = head;
head.next.prev = node;
head.next = node;
}
public void removeNode(DLinkedNode node) { // 删除节点
node.prev.next = node.next;
node.next.prev = node.prev;
}
public void moveToHead(DLinkedNode node) { // 节点已经存在,改变他的值,需要将该节点移动到头的位置
removeNode(node);
addToHead(node);
}
public DLinkedNode removeTail() { // size > capacity 需要删除末尾节点
DLinkedNode node = tail.prev;
removeNode(node);
return node;
}
}
// java自带的LinkedHashMap已经实现了LRU缓存
// class LRUCache extends LinkedHashMap<Integer, Integer> {
// private int capacity;
// public LRUCache(int capacity) {
// super(capacity, 0.75F, true); //调用父类的构造方法
// this.capacity = capacity;
// }
// public int get(int key) {
// return super.getOrDefault(key, -1);
// }
// public void put(int key, int value) {
// super.put(key, value);
// }
// @Override
// protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest){
// return size() > capacity;
// }
// }
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/