Java 实现 LRU缓存
LRU算法是一种常用的缓存算法,用于移除最近最少使用的数据。LRU缓存的核心思想是:当缓存容量已满,且需要插入新数据时,优先移除最近最少使用的数据。这种算法在许多场景下都非常有用,比如数据库缓存、网页缓存等。它可以帮助我们高效地管理有限的存储空间,同时保证最近访问的数据能够快速获取。
实现思路
为了实现一个高效的LRU缓存,我们需要结合两种数据结构:哈希表和双向链表。
- 哈希表:用于快速定位缓存中的数据。通过键(key)可以快速找到对应的节点。
- 双向链表:用于维护数据的访问顺序。最近访问的节点会被移动到链表的头部,而最久未访问的节点则位于链表的尾部。
这种结合方式的优势在于:
- 哈希表提供了快速的查找能力,时间复杂度为O(1)。
- 双向链表可以方便地移动节点,同时维护访问顺序。
以下是LRU缓存的完整实现代码:
public class LRUCache {private int capacity; // 缓存容量private Map<Integer, Node> map; // 哈希表,用于快速查找private Node head; // 双向链表的头节点private Node tail; // 双向链表的尾节点/*** 构造函数,初始化属性字段。虚拟的头节点head和尾节点tail可以简化链表操作。*/public LRUCache(int capacity) {this.capacity = capacity;map = new HashMap<>();head = new Node();tail = new Node();head.next = tail;tail.prev = head;}/*** 如果键存在于哈希表中,获取对应的节点,并将其移动到链表头部。如果键不存在,返回-1。*/public int get(int key) {if(map.containsKey(key)){Node node = map.get(key);move2First(node);return node.val;}return -1;}/*** 如果键已存在,更新节点的值,并将其移动到链表头部。* 如果键不存在,检查缓存是否已满:* 如果未满,创建新节点并插入。* 如果已满,移除链表尾部的节点(最久未访问的节点),并用新节点替换。*/public void put(int key, int value) {if(map.containsKey(key)){Node node = map.get(key);node.val = value;move2First(node);}else{Node node;if(map.size() < capacity) node = new Node(key, value);else{node = tail.prev;map.remove(node.key);node.val = value;node.key = key;}map.put(key, node);move2First(node);}}/*** 将节点移动到链表头部*/public void move2First(Node node){if(node == head.next) return;if(node.next != null) node.prev.next = node.next;if(node.prev != null) node.next.prev = node.prev;head.next.prev = node;node.next = head.next;node.prev = head;head.next = node;}// 测试案例public static void main(String[] args) {LRUCache cache = new LRUCache(2); // 缓存容量为2cache.put(1, 1);cache.put(2, 2);System.out.println(cache.get(1)); // 返回1cache.put(3, 3); // 移除键2System.out.println(cache.get(2)); // 返回-1(键2已被移除)cache.put(4, 4); // 移除键1System.out.println(cache.get(1)); // 返回-1(键1已被移除)System.out.println(cache.get(3)); // 返回3System.out.println(cache.get(4)); // 返回4}}// 双向链表节点
class Node{Node next;Node prev;int val;int key;Node(){ }Node(int key, int val){this.val = val;this.key = key;}
}