LRU是什么
LRU全名为Least Recently Used,直译成中文为“最近最少使用”,顾名思义,它的作用就是对缓存中的数据进行管理,将最近最少使用的数据清除,以保证缓存的命中率。这种算法的思想是基于“记忆力衰退”原理,即最近没有被访问的数据很可能在不久的将来也不会被访问。
假设你是一个小学生,每天你都要背诵一些东西,比如英语单词。但是你的脑袋只能记住一定数量的单词,当你脑海中的单词已经超过了这个数量时,就需要将一些没有用到过的单词移除,以便为新的单词腾出位置。这个过程就是LRU算法的思想。
自己设计一个LRU
设计要素
- 有限的空间(比如设置固定的CAP_SIZE)
- 双向链表
- map(存储key,value,通过O(1)复杂度查询)
代码
package algo;
import java.util.HashMap;
import java.util.Map;
public class LRU<K, V> {
private final int CAP_SIZE;
private Map<K, Node<K, V>> map;
private Node<K, V> head;
private Node<K, V> tail;
public LRU(int size) {
this.CAP_SIZE = size;
this.map = new HashMap<>();
this.head = null;
this.tail = null;
}
/**
* 将新节点插入到链表的头部。
* @param node
*/
public void addNode(Node<K, V> node) {
if (head == null) {
head = node;
tail = node;
} else {
node.next = head;
head.prev = node;
head = node;
}
}
/**
* 从链表中删除一个节点。
* @param node
*/
public void removeNode(Node<K, V> node) {
if (node == head && node == tail) {
head = null;
tail = null;
} else if (node == head) {
head = node.next;
head.prev = null;
} else if (node == tail){
tail = node.prev;
tail.next = null;
} else {
node.prev.next = node.next;
node.next.prev = node.prev;
}
}
/**
* 将新的键值对添加到缓存中。
* 如果键已经存在,则更新值,将相应的节点移动到链表的头部,并在必要时删除尾节点。
* 如果键不存在,则添加一个新节点到哈希表和链表的头部,并在必要时删除尾节点。
* @param key
* @param value
*/
public void put(K key, V value) {
Node<K, V> node = map.get(key);
if (null == node) {
node = new Node<>(key, value);
map.put(key, node);
if (map.size() > CAP_SIZE) {
map.remove(tail.key);
removeNode(tail);
}
} else {
node.val = value;
removeNode(node);
}
addNode(node);
}
/**
* 检索与给定键相关联的值。
* 如果键不存在于哈希表中,则返回null。
* 否则,将相应的节点移动到链表的头部,并返回其值。
* @param key
* @return
*/
public V get(K key) {
Node<K, V> node = map.get(key);
if (null == node) {
return null;
}
removeNode(node);
addNode(node);
return node.val;
}
private static class Node<K, V>{
K key;
V val;
Node<K, V> prev;
Node<K, V> next;
Node(K key, V val) {
this.key = key;
this.val = val;
prev = null;
next = null;
}
}
public static void main(String[] args) {
LRU<String, Integer> cache = new LRU<>(2);
cache.put("a", 1);
cache.put("b", 2);
System.out.println(cache.get("a")); // prints 1
cache.put("c", 3);
System.out.println(cache.get("b")); // prints null
cache.put("d", 4);
System.out.println(cache.get("a")); // prints null
System.out.println(cache.get("c")); // prints 3
System.out.println(cache.get("d")); // prints 4
}
}