LRU(最近最少使用)算法是一种常见的缓存淘汰策略,广泛应用于操作系统、数据库缓存、网页缓存等领域。该算法的核心思想是优先淘汰那些最长时间未被使用的数据,基于的假设是如果数据最近被访问过,那么将来被访问的可能性也更高。
LRU 算法的工作原理
-
数据结构:
- LRU算法通常利用一个双向链表(Linked List)和一个哈希表(Hash Map)来实现。链表用于表示缓存中的数据顺序,其中最近使用的数据排在链表头部,最久未使用的数据排在链表尾部。哈希表则存储每个数据的键与其在链表中的节点的对应关系,以支持快速的查找和更新操作。
-
访问数据:
- 当一个数据被访问时,如果数据在缓存中(哈希表能够找到),则将这个数据对应的节点在链表中移动到链表头部,表示最近被访问过。
- 如果数据不在缓存中,需要将数据加载到缓存。如果此时缓存未满,可以直接添加到链表头部并更新哈希表。如果缓存已满,则需要从链表尾部移除最久未使用的数据,并把新数据添加到链表头部。
-
淘汰数据:
- 当需要空间来存储新的数据时,算法会从链表的尾部移除最久未被访问的数据节点,并同步从哈希表中删除相应的键值对。
实现示例
在Java中实现LRU缓存,可以使用一个双向链表来跟踪最近最少使用的元素,同时使用一个哈希表来实现快速查找。下面是一个简单的Java实现示例:
首先,定义一个双向链表节点的类,该类包含键、值以及指向前一个节点和后一个节点的指针:
class Node {
int key;
int value;
Node prev;
Node next;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
然后,定义LRU缓存类,其中包含添加节点、移除节点、移动节点到头部、弹出尾部节点的方法,以及一个用于存储节点的哈希表和维护节点顺序的双向链表:
import java.util.HashMap;
public class LRUCache {
private HashMap<Integer, Node> map;
private int capacity, count;
private Node head, tail;
public LRUCache(int capacity) {
this.capacity = capacity;
map = new HashMap<>();
head = new Node(0, 0);
tail = new Node(0, 0);
head.next = tail;
tail.prev = head;
head.prev = null;
tail.next = null;
count = 0;
}
public void deleteNode(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
public void addToHead(Node node) {
node.next = head.next;
node.next.prev = node;
node.prev = head;
head.next = node;
}
public int get(int key) {
if (map.containsKey(key)) {
Node node = map.get(key);
deleteNode(node);
addToHead(node);
return node.value;
}
return -1;
}
public void put(int key, int value) {
if (map.containsKey(key)) {
Node node = map.get(key);
node.value = value;
deleteNode(node);
addToHead(node);
} else {
Node node = new Node(key, value);
map.put(key, node);
if (count < capacity) {
count++;
addToHead(node);
} else {
map.remove(tail.prev.key);
deleteNode(tail.prev);
addToHead(node);
}
}
}
}
在这个实现中:
Node
类定义了链表的节点。LRUCache
类包括一个哈希表和一个双向链表。get
方法和put
方法实现了LRU缓存的核心功能。addToHead
和deleteNode
方法用于节点的添加和删除操作。
这种结构确保了 get
和 put
操作的时间复杂度为 O(1),即常数时间复杂度,因为它们主要依赖于哈希表的操作和几个指针的改动。
总结
LRU缓存算法通过维护一个记录访问历史的列表来决定哪个数据项淘汰,使得长时间未被访问的数据最先被移除,这样可以有效地利用有限的缓存空间,确保缓存的高效使用。