1. LRU 原理简述
LRU(Least Recently Used)缓存的核心思想是:每次访问缓存时,都会将被访问的元素标记为“最新使用”;当缓存空间满时,淘汰最久未被访问的元素。
实现 LRU 通常用双向链表 + 哈希表:
- 哈希表用于 O(1) 查找元素。
- 双向链表用于维护访问顺序,头部是最近访问,尾部是最久未访问。
2. Java LRU Demo
下面是一个简单的 Java LRU 缓存实现(不依赖第三方库):
import java.util.*;
class LRUCache<K, V> {
private final int capacity;
private final Map<K, Node<K, V>> map;
private final DoubleLinkedList<K, V> list;
public LRUCache(int capacity) {
this.capacity = capacity;
this.map = new HashMap<>();
this.list = new DoubleLinkedList<>();
}
public V get(K key) {
if (!map.containsKey(key)) return null;
Node<K, V> node = map.get(key);
list.moveToHead(node);
return node.value;
}
public void put(K key, V value) {
if (map.containsKey(key)) {
Node<K, V> node = map.get(key);
node.value = value;
list.moveToHead(node);
} else {
if (map.size() >= capacity) {
Node<K, V> tail = list.removeTail();
if (tail != null) map.remove(tail.key);
}
Node<K, V> newNode = new Node<>(key, value);
list.addToHead(newNode);
map.put(key, newNode);
}
}
// 双向链表节点
static class Node<K, V> {
K key;
V value;
Node<K, V> prev, next;
Node(K key, V value) { this.key = key; this.value = value; }
}
// 双向链表
static class DoubleLinkedList<K, V> {
Node<K, V> head, tail;
void addToHead(Node<K, V> node) {
node.next = head;
node.prev = null;
if (head != null) head.prev = node;
head = node;
if (tail == null) tail = node;
}
void moveToHead(Node<K, V> node) {
if (node == head) return;
// remove node
if (node.prev != null) node.prev.next = node.next;
if (node.next != null) node.next.prev = node.prev;
if (node == tail) tail = node.prev;
// add to head
node.prev = null;
node.next = head;
if (head != null) head.prev = node;
head = node;
}
Node<K, V> removeTail() {
if (tail == null) return null;
Node<K, V> node = tail;
if (tail.prev != null) tail.prev.next = null;
tail = tail.prev;
if (tail == null) head = null;
return node;
}
}
}
3. 使用示例
public class Main {
public static void main(String[] args) {
LRUCache<Integer, String> cache = new LRUCache<>(2);
cache.put(1, "A");
cache.put(2, "B");
System.out.println(cache.get(1)); // 输出 A
cache.put(3, "C"); // 淘汰 key=2
System.out.println(cache.get(2)); // 输出 null
cache.put(4, "D"); // 淘汰 key=1
System.out.println(cache.get(1)); // 输出 null
System.out.println(cache.get(3)); // 输出 C
System.out.println(cache.get(4)); // 输出 D
}
}
4. 原理总结
- 每次访问元素(get/put),把元素移动到链表头部,表示最近被访问。
- 缓存满时,移除链表尾部元素(最久未被访问)。
- 哈希表保证查找和插入都是 O(1) 时间复杂度。
这种设计使得 LRU 缓存高效且易于扩展。