什么是LRU算法
LRU的全名为 Least Recently Used,意思是“最近少用”,是一种常用的页面置换算法,选择最近最久未使用的数据予以淘汰。典型的Redis缓存就采用了各类变种的LRU算法。
特性要求:
- 必须要有顺序之分,用来区分最近使用和很久没有使用的数据排序。
- 写和读操作一次完成
- 如果容量满了要删除最不常用的数据,每次新访问还要把新的数据插入到队头(左右自己设定)
- LRU的算法核心是哈希链表。哈希一次就能查到,满足查找快;增删用链表比较快。
用LinkedHashMap完成LRU算法
LinkedHashMap的源码都表示,这种 map 非常适合于构建LRU缓存。
class LRUCache extends LinkedHashMap<Integer, Integer> {
private int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75F, true);
this.capacity = capacity;
}
public int get(int key) {
return super.getOrDefault(key, -1);
}
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
}
手写LRU(不依赖JDK)
map负责查找,构建一个虚拟的双向链表,里面安装的是一个个的Node节点,作为数据载体。
Node节点:
// 1、构造一个Node节点,作为数据载体
class Node<K, V> {
K key;
V value;
Node<K, V> prev;
Node<K, V> next;
public Node() {
this.prev = this.next = null;
}
public Node(K key, V value) {
this.key = key;
this.value = value;
this.prev = this.next = null;
}
}
双向队列:
// 2、构造一个双向队列,里面存放的就是Node节点
class DoubleLinkedList<K, V> {
Node<K, V> head;
Node<K, V> tail;
// 构造方法
public DoubleLinkedList() {
head = new Node<>();
tail = new Node<>();
head.next = tail;
tail.prev = head;
}
// 添加到头
public void addHead(Node<K, V> node) {
node.next = head.next;
node.prev = head;
head.next.prev = node;
head.next = node;
}
// 删除节点
public void removeNode(Node<K, V> node) {
node.next.prev = node.prev;
node.prev.next = node.next;
node.prev = null;
node.next = null;
}
// 获得最后一个节点
public Node<K, V> getLast() {
return tail.prev;
}
}
实现LRU:
public class LRUCache {
private int capacity;
Map<Integer, Node<Integer, Integer>> map;
DoubleLinkedList<Integer, Integer> doubleLinkedList;
public LRUCache(int capacity) {
this.cacheSize = capacity;
map = new HashMap<>(); // 用于查找
doubleLinkedList = new DoubleLinkedList<>();
}
public int get(int key) {
if (!map.containsKey(key)) {
return -1;
}
Node<Integer, Integer> node = map.get(key);
// 先删掉原来的位置,再加入到头结点,就是最近使用的节点了
doubleLinkedList.removeNode(node);
doubleLinkedList.addHead(node);
return node.value;
}
public void put(int key, int value) {
// 如果存在,则替换新的value,再放回map
if (map.containsKey(key)) {
Node<Integer, Integer> node = map.get(key);
node.value = value;
map.put(key, node);
doubleLinkedList.removeNode(node);
doubleLinkedList.addHead(node);
} else {
// 坑位满了
if (map.size() == capacity) {
Node<Integer, Integer> lastNode = doubleLinkedList.getLast();
map.remove(lastNode.key);
doubleLinkedList.removeNode(lastNode);
}
// 坑位没满,新增
Node<Integer, Integer> newNode = new Node<>(key, value);
map.put(key, newNode);
doubleLinkedList.addHead(newNode);
}
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。