Java手写LRU缓存算法
1. 算法思维导图
2. 实现原理
LRU(Least Recently Used)缓存算法是一种常用的缓存淘汰策略,它的基本思想是根据数据的访问时间来进行淘汰,最近使用的数据被认为是将来也可能会被使用的,因此被保留,而较久未使用的数据被认为是将来可能不会再使用的,因此被淘汰。
3. 手写必要性
手写LRU缓存算法的必要性在于深入理解算法的实现原理和核心思想,同时可以根据实际需求进行优化和定制化,提高缓存的效率和命中率。
4. 市场调查
根据市场调查,LRU缓存算法在各个领域都有广泛的应用,特别适用于需要频繁读取和更新数据的场景,如数据库查询、网络请求、页面缓存等。同时,随着大数据和云计算的发展,对高效缓存算法的需求也越来越大。
5. 实现介绍
5.1 详细步骤
- 初始化缓存大小和哈希表
- 定义双向链表节点类
- 定义LRU缓存类,包括构造函数和核心方法
- 实现核心方法:
- 查询缓存:根据键值在哈希表中查找对应的节点,若存在则将节点移动到链表头部,并返回值;若不存在则返回-1。
- 更新缓存:根据键值在哈希表中查找对应的节点,若存在则更新节点值并将节点移动到链表头部;若不存在则创建新节点并将节点插入链表头部,同时在哈希表中添加新节点。
- 插入缓存:根据键值在哈希表中查找对应的节点,若存在则更新节点值并将节点移动到链表头部;若不存在则创建新节点并将节点插入链表头部,同时在哈希表中添加新节点。若缓存已满,则删除链表尾部节点,并在哈希表中删除对应的节点。
- 删除缓存:根据键值在哈希表中查找对应的节点,若存在则删除节点,并在哈希表中删除对应的节点。
5.2 代码实现
// 双向链表节点类
class Node {
int key;
int value;
Node prev;
Node next;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
// LRU缓存类
class LRUCache {
private int capacity;
private Map<Integer, Node> cache;
private Node head;
private Node tail;
public LRUCache(int capacity) {
this.capacity = capacity;
this.cache = new HashMap<>();
this.head = new Node(0, 0);
this.tail = new Node(0, 0);
this.head.next = this.tail;
this.tail.prev = this.head;
}
public int get(int key) {
if (cache.containsKey(key)) {
Node node = cache.get(key);
moveToHead(node);
return node.value;
}
return -1;
}
public void put(int key, int value) {
if (cache.containsKey(key)) {
Node node = cache.get(key);
node.value = value;
moveToHead(node);
} else {
Node newNode = new Node(key, value);
cache.put(key, newNode);
addToHead(newNode);
if (cache.size() > capacity) {
Node tailNode = removeTail();
cache.remove(tailNode.key);
}
}
}
private void moveToHead(Node node) {
removeNode(node);
addToHead(node);
}
private void addToHead(Node node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private Node removeTail() {
Node tailNode = tail.prev;
removeNode(tailNode);
return tailNode;
}
}
6. 总结与思维拓展
通过手写LRU缓存算法,我们深入理解了其实现原理和核心思想。LRU缓存算法可以提高缓存的效率和命中率,适用于各种需要频繁读取和更新数据的场景。在实际应用中,我们可以根据具体需求进行优化和定制化,进一步提升算法的性能。
7. 完整代码
// 双向链表节点类
class Node {
int key;
int value;
Node prev;
Node next;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
// LRU缓存类
class LRUCache {
private int capacity;
private Map<Integer, Node> cache;
private Node head;
private Node tail;
public LRUCache(int capacity) {
this.capacity = capacity;
this.cache = new HashMap<>();
this.head = new Node(0, 0);
this.tail = new Node(0, 0);
this.head.next = this.tail;
this.tail.prev = this.head;
}
public int get(int key) {
if (cache.containsKey(key)) {
Node node = cache.get(key);
moveToHead(node);
return node.value;
}
return -1;
}
public void put(int key, int value){
if (cache.containsKey(key)) {
Node node = cache.get(key);
node.value = value;
moveToHead(node);
} else {
Node newNode = new Node(key, value);
cache.put(key, newNode);
addToHead(newNode);
if (cache.size() > capacity) {
Node tailNode = removeTail();
cache.remove(tailNode.key);
}
}
}
private void moveToHead(Node node) {
removeNode(node);
addToHead(node);
}
private void addToHead(Node node) {
node.prev = head;
node.next = head.next;
head.next.prev = node;
head.next = node;
}
private void removeNode(Node node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
private Node removeTail() {
Node tailNode = tail.prev;
removeNode(tailNode);
return tailNode;
}
}