LRUCache实现
在面试中,我们经常听到说LRU这个关键词,那么我们需要了解什么是LRU,我的理解是,LRU是一种淘汰策略,我们都知道操作系统的内存更替策略、Redis的缓存淘汰策略都有它的一个影子。那么要知其所以然,我们还得需要手工实现它,那么这里我给出一个实现是基于双端链表+哈希表的,这里并没有考虑到并发的场景,如果读者想去优化,可以给缓存加入读写锁ReentrantReadWriteLock
。
public static class LRUCache {
private static class ListNode {
int key;
int val;
ListNode prev;
ListNode next;
public ListNode() {
}
public ListNode(int key, int val) {
this.key = key;
this.val = val;
}
}
private final Map<Integer, ListNode> map = new HashMap<>();
private final ListNode head = new ListNode();
private final ListNode tail = new ListNode();
private final int capacity;
private int size;
public LRUCache(int capacity) {
this.capacity = capacity;
head.next = tail;
tail.prev = head;
}
public int get(int key) {
ListNode node = map.getOrDefault(key, null);
if (node == null) {
return -1;
}
remNode(node);
addHead(node);
return node.val;
}
public void put(int key, int value) {
ListNode node = map.getOrDefault(key, null);
if (node == null) {
node = new ListNode(key, value);
if (size >= capacity) {
// remove list's tail node
ListNode oldTail = tail.prev;
remNode(oldTail);
// remove map's element
map.remove(oldTail.key);
size--;
}
addHead(node);
map.put(key, node);
size++;
} else {
node.val = value;
remNode(node);
addHead(node);
}
}
private void addHead(ListNode node) {
ListNode oldHead = head.next;
// add node to list's head
head.next = node;
node.prev = head;
node.next = oldHead;
oldHead.prev = node;
}
private void remNode(ListNode node) {
ListNode next = node.next;
ListNode prev = node.prev;
// remove node in list
next.prev = prev;
prev.next = next;
// help gc
node.next = null;
node.prev = null;
}
}