实现 Java HashMap LRU
简介
LRU(Least Recently Used,即最近最少使用)是一种常见的缓存淘汰策略,它的原理是根据数据的访问时间来决定数据是否被淘汰。在 Java 中,我们可以通过使用 HashMap 和双向链表来实现 LRU 缓存。
实现步骤
下面是实现 Java HashMap LRU 的步骤:
步骤 | 操作 |
---|---|
1 | 创建一个 HashMap 用于存储数据 |
2 | 创建一个双向链表用于维护数据的访问顺序 |
3 | 当访问一个数据时,如果数据已经存在于 HashMap 中,则将其从链表中移除并放到链表头部;如果数据不存在于 HashMap 中,则将其添加到链表头部 |
4 | 当链表的长度超过设定的阈值时,将链表尾部的数据从 HashMap 和链表中移除 |
接下来我们一步一步实现上述步骤。
创建 HashMap 和双向链表
首先,我们需要创建一个 HashMap 和一个双向链表。Java 中提供了相应的类可以直接使用,所以我们只需要实例化它们即可。
import java.util.HashMap;
import java.util.Map;
public class LRUCache<K, V> {
private Map<K, Node<K, V>> cache;
private Node<K, V> head;
private Node<K, V> tail;
private int capacity;
public LRUCache(int capacity) {
this.cache = new HashMap<>();
this.head = new Node<>();
this.tail = new Node<>();
this.head.next = tail;
this.tail.prev = head;
this.capacity = capacity;
}
}
class Node<K, V> {
K key;
V value;
Node<K, V> prev;
Node<K, V> next;
}
在上述代码中,我们定义了一个泛型类 LRUCache
,其中 K
表示键的类型,V
表示值的类型。cache
是用于存储数据的 HashMap,head
和 tail
分别表示双向链表的头部和尾部。capacity
表示缓存容量。
访问数据
当访问一个数据时,我们需要判断该数据是否已经存在于 HashMap 中。如果存在,我们需要将其从链表中移除并放到链表头部;如果不存在,我们需要将其添加到链表头部。
public V get(K key) {
Node<K, V> node = cache.get(key);
if (node == null) {
return null;
}
// 将访问的数据移到链表头部
moveToHead(node);
return node.value;
}
public void put(K key, V value) {
Node<K, V> node = cache.get(key);
if (node == null) {
// 如果数据不存在,创建一个新的节点,并将其添加到链表头部和 HashMap 中
Node<K, V> newNode = new Node<>();
newNode.key = key;
newNode.value = value;
cache.put(key, newNode);
addToHead(newNode);
if (cache.size() > capacity) {
// 如果链表长度超过容量,移除链表尾部的节点
Node<K, V> tailNode = removeTail();
cache.remove(tailNode.key);
}
} else {
// 如果数据已经存在,更新其值,并将其移动到链表头部
node.value = value;
moveToHead(node);
}
}
private void moveToHead(Node<K, V> node) {
removeNode(node);
addToHead(node);
}
private void removeNode(Node<K, V> node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private void addToHead(Node<K, V> node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private Node<K, V> removeTail() {
Node<K, V> tailNode = tail.prev;
removeNode(tailNode);
return tailNode;
}
在上述代码中,get
方法用于获取数据,put
方法用于插入或更新数据。moveToHead
方法用于将访问的数据移到链表头部,removeNode
方法用于从链表中移除节点,addToHead
方法用于将节点添加到链表头部,removeTail
方法用于移除链表尾部