LRU Cache
Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get
and set
.
get(key)
- Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(key, value)
- Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.
Analysis
cache is used to store the last n requests and responses to them from the system. When the requests comes, looking it up in the cache, if hits, there is no need to pass the request to the system. Otherwise, passing the request to the system.
use a hash map to look up the requests and use a double linked list to connect the block in cache. Every time a block is manipulated, move it to the head of the double linked list so that ensuring the sequence of the double linked list is from newest to oldest.
//Set: If the key already exists in the cache, modify the value and move it to the front of the cache. Otherwise, when the capacity is full, delete the last block and insert the new block into the front the the double linked list. Otherwise, insert the new block into the front of the double linked list directly.
Solution
public class LRUCache {
private class Node {
Node prev;
Node next;
int value;
int key;
public Node(int key, int value){
this.key = key;
this.value = value;
}
}
HashMap<Integer, Node> map;
int capacity;
Node head = new Node(-1, -1);
Node tail = new Node(-1, -1);
public LRUCache(int capacity) {
map = new HashMap<Integer, Node>();
this.capacity = capacity;
head.next = tail;
head.prev = null;
tail.prev = head;
tail.next = null;
}
public int get(int key) {
if (map.containsKey(key)) {
Node latest = map.get(key);
latest.prev.next = latest.next;
latest.next.prev = latest.prev;
latest.next = head.next;
head.next.prev = latest;
head.next = latest;
latest.prev = head;
return latest.value;
} else {
return -1;
}
}
public void set(int key, int value) {
if (map.containsKey(key)) {
Node latest = map.get(key);
latest.value = value;
latest.prev.next = latest.next;
latest.next.prev = latest.prev;
latest.next = head.next;
head.next.prev = latest;
head.next = latest;
latest.prev = head;
} else {
Node newNode = new Node(key, value);
if (map.size() == capacity) {
map.remove(tail.prev.key);
tail.prev.prev.next = tail;
tail.prev = tail.prev.prev;
newNode.next = head.next;
head.next.prev = newNode;
head.next = newNode;
newNode.prev = head;
map.put(key, newNode);
} else {
map.put(key, newNode);
newNode.next = head.next;
head.next.prev = newNode;
head.next = newNode;
newNode.prev = head;
}
}
}
}
public class LRUCache {
private class Node {
Node prev;
Node next;
int value;
int key;
public Node(int key, int value){
this.key = key;
this.value = value;
}
}
HashMap<Integer, Node> map;
int capacity;
Node head = new Node(-1, -1);
Node tail = new Node(-1, -1);
public LRUCache(int capacity) {
map = new HashMap<Integer, Node>();
this.capacity = capacity;
head.next = tail;
head.prev = null;
tail.prev = head;
tail.next = null;
}
public int get(int key) {
if (map.containsKey(key)) {
Node latest = map.get(key);
latest.prev.next = latest.next;
latest.next.prev = latest.prev;
latest.next = head.next;
head.next.prev = latest;
head.next = latest;
latest.prev = head;
return latest.value;
} else {
return -1;
}
}
public void set(int key, int value) {
if (map.containsKey(key)) {
Node latest = map.get(key);
latest.value = value;
latest.prev.next = latest.next;
latest.next.prev = latest.prev;
latest.next = head.next;
head.next.prev = latest;
head.next = latest;
latest.prev = head;
} else {
Node newNode = new Node(key, value);
if (map.size() == capacity) {
map.remove(tail.prev.key);
tail.prev.prev.next = tail;
tail.prev = tail.prev.prev;
newNode.next = head.next;
head.next.prev = newNode;
head.next = newNode;
newNode.prev = head;
map.put(key, newNode);
} else {
map.put(key, newNode);
newNode.next = head.next;
head.next.prev = newNode;
head.next = newNode;
newNode.prev = head;
}
}
}
}