Java实现LRU(最近最少使用)算法
一、引言
LRU(Least Recently Used)算法,即最近最少使用算法,是一种常见的缓存淘汰策略。它基于局部性原理,即最近被访问的数据在未来一段时间内被访问的概率更高,而长时间未被访问的数据在未来一段时间内被访问的概率较低。在Java中实现LRU算法,可以有效地管理缓存数据,提高系统性能。
二、实现方式
在Java中实现LRU算法主要有两种方式:使用LinkedHashMap
和自定义双向链表结合HashMap
。
1、使用LinkedHashMap
Java的LinkedHashMap
类提供了一个按访问顺序排序的功能,可以用来实现LRU缓存。通过设置accessOrder
为true
,LinkedHashMap
会将最近访问的元素移动到链表的末尾,而最老的元素(链表的头部)则是最少被访问的元素。
1.1、步骤
- 创建一个
LinkedHashMap
实例,并设置一个初始容量和加载因子。 - 通过
put
和get
方法操作缓存,LinkedHashMap
会自动维护元素的访问顺序。 - 重写
removeEldestEntry
方法,当缓存容量超出限制时,自动移除最老的元素。
2、自定义双向链表结合HashMap
这种方式需要自己实现一个双向链表来维护缓存数据的顺序,同时使用HashMap
来存储键和对应节点的引用,以实现快速查找。
2.1、步骤
- 定义一个双向链表节点类,包含键、值、前驱和后继指针。
- 创建一个缓存类,内部包含一个
HashMap
和一个双向链表。 - 在
get
和put
方法中,根据访问情况更新节点在链表中的位置。 - 当缓存容量超出限制时,移除链表尾部的节点,即最老的元素。
三、代码实现
1、使用LinkedHashMap
java
/**
* @author ning0
* @date 2024/9/23 12:03
* @description Main
**/
import java.util.LinkedHashMap;
import java.util.Map;
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private final int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75f, true);
this.capacity = capacity;
}
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > capacity;
}
public static void main(String[] args) {
LRUCache<Integer, String> cache = new LRUCache<>(3);
cache.put(1, "one");
cache.put(2, "two");
cache.put(3, "three");
cache.get(2); // 访问2,将其移到链表尾部
cache.put(4, "four"); // 容量超出,移除最老的元素1
System.out.println(cache.keySet()); // 输出:[3, 2, 4]
}
}
2、自定义双向链表结合HashMap
java
/**
* @author ning0
* @date 2024/9/23 12:03
* @description Main
**/
import java.util.HashMap;
import java.util.Map;
class LRUCache<K, V> {
private final int capacity;
private final Map<K, Node<K, V>> map;
private final Node<K, V> head, tail;
public LRUCache(int capacity) {
this.capacity = capacity;
map = new HashMap<>();
head = new Node<>(null, null);
tail = new Node<>(null, null);
head.next = tail;
tail.prev = head;
}
public V get(K key) {
Node<K, V> node = map.get(key);
if (node == null) return null;
moveToHead(node);
return node.value;
}
public void put(K key, V value) {
Node<K, V> node = map.get(key);
if (node == null) {
node = new Node<>(key, value);
map.put(key, node);
addNode(node);
if (map.size() > capacity) {
map.remove(tail.prev.key);
removeNode(tail.prev);
}
} else {
node.value = value;
moveToHead(node);
}
}
private void addNode(Node<K, V> node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(Node<K, V> node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void moveToHead(Node<K, V> node) {
removeNode(node);
addNode(node);
}
class Node<K, V> {
K key;
V value;
Node<K, V> prev;
Node<K, V> next;
Node(K key, V value) {
this.key = key;
this.value = value;
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder("[");
Node<K, V> curr = head.next;
while (curr != tail) {
sb.append(curr.key).append(", ");
curr = curr.next;
}
return sb.delete(sb.length() - 2, sb.length()).append("]").toString();
}
public static void main(String[] args) {
LRUCache<Integer, String> cache = new LRUCache<>(3);
cache.put(1, "one");
cache.put(2, "two");
cache.put(3, "three");
cache.get(2); // 访问2,将其移到链表头部
cache.put(4, "four"); // 容量超出,移除最老的元素1
System.out.println(cache); // 输出:[4, 2, 3]
}
}
四、总结
LRU算法在缓存管理中扮演着重要角色,通过合理地淘汰最不常访问的数据,可以提高缓存的利用率和系统的性能。Java中实现LRU算法的两种方式各有优势,LinkedHashMap
实现简单快捷,而自定义双向链表结合HashMap
则提供了更高的灵活性。开发者可以根据实际需求选择合适的实现方式。
版权声明:本博客内容为原创,转载请保留原文链接及作者信息。
参考文章: