LRU 缓存机制可以通过哈希表辅以双向链表实现,我们用一个哈希表和一个双向链表维护所有在缓存中的键值对。
双向链表按照被使用的顺序存储了这些键值对,靠近头部的键值对是最近使用的,而靠近尾部的键值对是最久未使用的。
哈希表即为普通的哈希映射(HashMap),通过缓存数据的键映射到其在双向链表中的位置。
这样以来,我们首先使用哈希表进行定位,找出缓存项在双向链表中的位置,随后将其移动到双向链表的头部,即可在 O(1)O(1) 的时间内完成 get 或者 put 操作。具体的方法如下:
对于 get 操作,首先判断 key 是否存在:
如果 key 不存在,则返回 -1−1;
如果 key 存在,则 key 对应的节点是最近被使用的节点。通过哈希表定位到该节点在双向链表中的位置,并将其移动到双向链表的头部,最后返回该节点的值。
对于 put 操作,首先判断 key 是否存在:
如果 key 不存在,使用 key 和 value 创建一个新的节点,在双向链表的头部添加该节点,并将 key 和该节点添加进哈希表中。然后判断双向链表的节点数是否超出容量,如果超出容量,则删除双向链表的尾部节点,并删除哈希表中对应的项;
如果 key 存在,则与 get 操作类似,先通过哈希表定位,再将对应的节点的值更新为 value,并将该节点移到双向链表的头部。
class DLinkedNode{
int key;
int value;
DLinkedNode pre;
DLinkedNode next;
public DLinkedNode(){
}
public DLinkedNode(int key,int value){
this.key = key;
this.value = value;
}
}
class LRUCache {
private HashMap<Integer,DLinkedNode> map = new HashMap();
private int capacity;
private DLinkedNode first,last;//头部为热点数据,尾部为要过期的数据
public LRUCache(int capacity) {
this.capacity = capacity;
first = new DLinkedNode();
last = new DLinkedNode();
first.next = last;
last.pre = first;
}
public int get(int key) {
if(map.containsKey(key)){
DLinkedNode node = map.get(key);
//1.从链表移除
node.pre.next = node.next;
node.next.pre = node.pre;
//2.插入链表头部
node.next = first.next;
node.pre = first;
first.next.pre = node;
first.next = node;
return node.value;
}
return -1;
}
public void put(int key, int value) {
if(map.containsKey(key)){
DLinkedNode node = map.get(key);
//1.从链表移除
removeNode(node);
//2.插入链表头部
putFirstNode(node);
//覆盖原来的值
node.value = value;
}else{
if(capacity>0){
//创建node
DLinkedNode node = new DLinkedNode(key,value);
//插入链表头部
putFirstNode(node);
//把node插入map
map.put(key,node);
//容量减一
capacity--;
}else{
//删除过期数据
DLinkedNode node = last.pre;
map.remove(node.key);
//移除node
removeNode(node);
//创建新node
node = new DLinkedNode(key,value);
//插入链表头部
putFirstNode(node);
//插入map
map.put(key,node);
}
}
}
private void removeNode(DLinkedNode node){
node.pre.next = node.next;
node.next.pre = node.pre;
}
public void putFirstNode(DLinkedNode node){
node.next = first.next;
node.pre = first;
first.next.pre = node;
first.next = node;
}
}