Java实现LRU缓存
问题描述:LRU是Least Recently Used 的缩写,翻译过来就是“最近最少使用”,LRU缓存就是使用这种原理实现,简单的说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉,比如我们缓存10条数据,当数据小于10时可以随意添加,当超过10时就需要把新的数据添加进来,同时要把过期数据删除,以确保我们最大缓存10条,每次将被访问数据放在cache最前端,以保证cache中数据按被访问的先后顺排列。
解决思路
由于有删除、插入和移动操作,因此选择链表实现。为了提高操作效率,采用双向链表,并用head和tail记录链表的头尾节点。
在进行查询的时候,由于链表时间复杂度为O(n),因此使用HashMap辅助降低查询的时间复杂度。
1.查询: 直接从HashMap中获取;
2.删除: 先从HashMap中删除,再删除其尾节点;
3.插入: 先放入HashMap,在插入为头结点;
4.移动: 将指定节点移动为头结点
代码及注释如下
import java.util.HashMap;
public class MyLRU {
//记录cache中已缓存元素个数
private int length = 0;
//双向链表的头指针和尾指针
private DoubleLinkedNode head;
private DoubleLinkedNode tail;
//存储元素的map,其中key为DoubleLinkedNode的key,value 为DoubleLinkedNode
HashMap<Integer, DoubleLinkedNode> nodeMap = new HashMap();
//根据key值从cache中获取value
public int get(int key) {
int value = -1;
//如果cache中包含该元素,则返回,否则返回 -1
if (nodeMap.containsKey(key)) {
//从map中获取节点
DoubleLinkedNode node = nodeMap.get(key);
//将节点放到队首。如果head == tail则说明只有一个节点,则无需处理。
if(head != tail){
//如果node.pre == null,则说明其为头结点,也不用处理
if (node.pre != null) {
//处理其后续节点
if (node.next != null) {
//如果有后续节点,则需要将后续节点的前指针指向其前节点
node.next.pre = node.pre;
}else{
//如果node.next==null,则其为尾节点,其移动到头结点,
//会对尾节点tail产生影响,因此需要先处理tail
tail = tail.pre;
}
//处理前节点:其后节点的前指针指向其前节点;
node.pre.next = node.next;
//将其插入到头部
node.next = head;
head.pre = node;
head = node;
}
}
//取值
value = head.value;
}
showAllValues();
return value;
}
//向cache中新增元素
public void put(int key, int value) {
DoubleLinkedNode node = new DoubleLinkedNode(key, value);
nodeMap.put(key, node);
if(head == null){
head = node;
tail = node;
}else{
if(length >= 5){
nodeMap.remove(tail);
tail = tail.pre;
tail.next = null;
length--;
}
node.next = head;
head.pre = node;
head = node;
}
length++;
showAllValues();
}
//按顺序输出cache中现有的元素,可用于测试算法是否正确,无实际意义
private void showAllValues() {
DoubleLinkedNode node = head;
while (node != null) {
System.out.print(node.key + " ");
node = node.next;
}
System.out.println();
}
//节点
private class DoubleLinkedNode {
int key;
int value;
DoubleLinkedNode pre;
DoubleLinkedNode next;
public DoubleLinkedNode(int key, int value) {
this.key = key;
this.value = value;
}
}
}