文章目录
- 什么是LRU Cache?
- LRU Cache的实现
- JDK中自带的数据结构
- 模拟实现LRU Cache(双向链表+哈希表)
什么是LRU Cache?
LRU的全称是“Least Recently Used”的缩写,表示最近最少的使用,是一种Cache替换算法;而Cache其实就类似与我们所说的缓存,Cache的容量是有限的,当容量用完之后,如果又有新的内容需要添加进来的时候,就需要挑选并舍弃原有的部分内容,进而腾出空间来存放新的内容。
总而言之,LRU Cache的替换原则就是将最近最少使用的内容替换掉。
LRU Cache的实现
实现LRU Cache的方法和思路有很多,但是要保持高效的put和get,比如说要使时间复杂度为O(1)的话,那么只有一种方法(本章所总结的)— 使用双向链表和哈希表实现(最高效、最经典),原因是使用双向链表可以实现任意位置的插入和删除都保持在O(1)的时间复杂度,使用哈希表可以作为缓存,对其进行增删改查的时间复杂度也是保持在O(1)的。
JDK中自带的数据结构
在自己模拟实现LRU Cache之前,先来简单总结一下JDK中自带的实现LRU Cache的数据结构。
JDK中,需要使用util包下的LinkedHashMap类。
基于插入顺序:这其实就相当于一个单调队列,并没有将最近访问的元素放到双向链表的最后。
基于访问顺序:一般情况下都是使用这种,原因是这种才是真正意义上的LRU Cache。
LRU Cache的类型题:LRU缓存
class LRUCache extends LinkedHashMap<Integer, Integer>{
public int capacity;
public LRUCache(int capacity) {
super(capacity, 0.75f, true);
this.capacity = capacity;
}
public int get(int key) {
return super.getOrDefault(key, -1);
}
public void put(int key, int value) {
super.put(key, value);
}
@Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
}
模拟实现LRU Cache(双向链表+哈希表)
import java.util.HashMap;
import java.util.Map;
public class MyLRUCache {
static class DLinkNode{
public int key;
public int value;
public DLinkNode prev;
public DLinkNode next;
public DLinkNode(){
}
public DLinkNode(int key, int value){
this.key = key;
this.value = value;
}
}
public DLinkNode head; //双向链表的头结点
public DLinkNode tail; //双向链表的尾结点
public int usedSize; //当前链表中的有效节点个数
public Map<Integer, DLinkNode> map;
public int capacity; //容量
public MyLRUCache(int capacity){
this.head = new DLinkNode();
this.tail = new DLinkNode();
this.map = new HashMap<>();
this.capacity = capacity;
head.next = tail;
tail.prev = head;
}
//存储元素
public void put(int key, int val){
//查找key是否被存储过
DLinkNode node = map.get(key);
if(node == null){
DLinkNode dLinkNode = new DLinkNode(key, val);
map.put(key, dLinkNode);
addToTail(dLinkNode);
usedSize++;
if(usedSize > capacity){
map.remove(head.next.key);
removeNode(head.next);
usedSize--;
}
}else{
node.value = val;
moveToTail(node);
}
}
//将当前节点移到尾巴节点
private void moveToTail(DLinkNode node) {
removeNode(node);
addToTail(node);
}
//删除指定节点
private void removeNode(DLinkNode node) {
node.prev.next = node.next;
node.next.prev = node.prev;
}
//添加节点到链表的尾部
private void addToTail(DLinkNode node) {
tail.prev.next = node;
node.prev = tail.prev;
node.next = tail;
tail.prev = node;
}
//访问元素
public int get(int key){
DLinkNode node = map.get(key);
if(node == null){
return -1;
}else{
moveToTail(node);
return node.value;
}
}
}
以上代码仅供参考。