LRUCache实现【Java】
import java.util.HashMap;
import java.util.Map;
public class LRUCache<K, V> {
private Map<K, Node<K,V>> map = new HashMap<>();
private Node<K,V> head;
private Node<K,V> tail;
private int capacity;
private int pos;
public LRUCache(int capacity) {
this.head = new Node<>(null, null, null, null);
this.tail = new Node<>(null, null, null, null);
this.head.next = this.tail;
this.tail.pre = this.head;
this.capacity = capacity;
}
static class Node<K, V> {
private K key;
private V val;
private Node<K,V> pre;
private Node<K,V> next;
public Node(K key, V val, Node<K,V> pre, Node<K,V> next) {
this.key = key;
this.val = val;
this.pre = pre;
this.next = next;
}
}
public V get(K key){
if(map.containsKey(key)){
Node<K,V> valNode = map.get(key);
moveToHead(valNode);
return valNode.val;
}
return null;
}
public V put(K key, V value){
if(map.containsKey(key)){
Node<K,V> valNode = map.get(key);
valNode.val = value;
moveToHead(valNode);
} else {
Node<K,V> newNode = new Node<>(key, value, null,null);
map.put(key, newNode);
addToHead(newNode);
drainOutIfNesesary();
}
return null;
}
public void drainOutIfNesesary(){
if(pos > capacity){
map.remove(tail.pre.key);
tail.pre.pre.next = tail;
tail.pre = tail.pre.pre;
pos--;
}
}
public void addToHead(Node<K,V> newNode){
newNode.next = head.next;
head.next.pre = newNode;
head.next = newNode;
newNode.pre = head;
pos++;
}
public V remove(K key){
if(map.containsKey(key)){
Node<K,V> node = map.get(key);
node.next.pre = node.pre;
node.pre.next = node.next;
map.remove(key);
pos--;
return node.val;
}
return null;
}
private void moveToHead(Node<K,V> valNode){
valNode.next.pre = valNode.pre;
valNode.pre.next = valNode.next;
head.next.pre = valNode;
valNode.next = head.next;
head.next = valNode;
valNode.pre = head;
}
public static void main(String[] args) {
LRUCache<String, Integer> cache = new LRUCache<>(3);
cache.put("a",1);
cache.put("b",2);
cache.put("c",3);
cache.put("d", 4);
cache.put("e", 5);
print(cache);
cache.remove("d");
print(cache);
cache.put("f", 6);
cache.put("g", 7);
print(cache);
cache.get("f");
print(cache);
cache.put("e", 10);
print(cache);
}
static void print(LRUCache cache){
for(Node curNode = cache.head.next; !curNode.equals(cache.tail); curNode = curNode.next){
System.out.print(curNode.val+", ");
}
System.out.println();
}
}
Redis LRU Cache实现
如果按照HashMap和双向链表实现,需要额外的存储存放 next 和 prev 指针,牺牲比较大的存储空间 Redis策略:随机取出若干个key,然后按照访问时间排序后,淘汰掉最不经常使用的 LRU淘汰策略:
- volatile-lru 设置了过期时间的key参与近似的lru淘汰策略
- allkeys-lru 所有的key均参与近似的lru淘汰策略 Redis会基于server.maxmemory_samples配置选取固定数目的key,然后比较它们的lru访问时间,然后淘汰最近最久没有访问的key。maxmemory_samples的值越大,Redis的近似LRU算法就越接近于严格LRU算法,但是相应消耗也变高,对性能有一定影响,样本值默认为5。