本文已参与掘金创作者训练营第三期「话题写作」赛道,详情查看:掘力计划|创作者训练营第三期正在进行,「写」出个人影响力。
Code皮皮虾 一个沙雕而又有趣的憨憨少年,和大多数小伙伴们一样喜欢听歌、游戏,当然除此之外还有写作的兴趣,emm...,日子还很长,让我们一起加油努力叭🌈
欢迎各位小伙伴们关注我的公众号:JavaCodes,名称虽带Java但涉及范围可不止Java领域噢😁,会长期分享博文或者福利,期待您的关注❤
😉毛遂自荐
毛遂自荐,给大家推荐一下自己的专栏😁,欢迎小伙伴们收藏关注😊
✨LRU介绍
根据百度百科的介绍:LRU_百度百科 (baidu.com)
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
那么按照我的话来说就是:淘汰最近最少使用到的数据
☀LRU使用场景
- 操作系统的LRU页面置换算法
- Mysql缓冲池使用的LRU算法对数据页进行管理
- Redis的缓存淘汰策略也有
volatile-lru 和 allkeys-lru
都是使用到的LRU算法
🔥实现LRU算法
🔥实现LRU——LRU相关练习题
可见该题的点赞数和评论、题解量都是非常之高
🔥实现LRU——前提概要讲解✨
题目已经说过:如何用链表来实现 LRU 缓存淘汰策略?
那么对于实现LRU所使用的链表不是普通链表而是双向链表
而且在面试过程中,面试官不会让你使用已有的库,例如:LinkedList之类底层使用链表的集合类来实现LRU算法
所以对于双向链表我们需要手动实现
class DoubleLinkedList {
int key;
int value;
DoubleLinkedList prev;
DoubleLinkedList next;
public DoubleLinkedList() {
}
public DoubleLinkedList(int key, int value) {
this.key = key;
this.value = value;
}
}
除此之外,我们还定义虚拟头节点、尾节点来规定区间
🔥而且,从头结点到尾节点使用次数逐渐减少,也就是说从最近最多使用逐渐到最近最少使用!!!
private DoubleLinkedList head,tail;
光有双向链表可还不够,因为LRU是淘汰最近最少使用,那么还需要记录最少使用
那么,我们使用HashMap来记录使用的次数
private Map<Integer,DoubleLinkedList> map;
☀前提概要大概就这些了,那么接下来我们进入完整代码流程部分
🔥实现LRU——完整代码注释讲解✨
import java.util.HashMap;
import java.util.Map;
public class LruDemo {
//定义双向链表
class DoubleLinkedList {
int key;
int value;
DoubleLinkedList prev;
DoubleLinkedList next;
public DoubleLinkedList() {
}
public DoubleLinkedList(int key, int value) {
this.key = key;
this.value = value;
}
}
//当前容量
private int size;
//最大容量
private int capacity;
//记录使用次数
private Map<Integer,DoubleLinkedList> map;
//虚拟头节点、尾节点
private DoubleLinkedList head,tail;
//构造函数进行初始化
public LruDemo(int capacity) {
this.size = 0;
this.capacity = capacity;
this.map = new HashMap<>();
// 使用伪头部和伪尾部节点
this.head = new DoubleLinkedList();
this.tail = new DoubleLinkedList();
head.next = tail;
tail.prev = head;
}
//LRU——get方法
public int get(int key) {
//如果不存在key
if (!map.containsKey(key)) {
return -1;
}
DoubleLinkedList node = map.get(key);
// 如果 key 存在,先通过哈希表定位,再移到头部(因为他是最近使用到的,所以要放到头节点)
moveToHead(node);
return node.value;
}
//LRU——put方法
public void put(int key,int value) {
DoubleLinkedList node = map.get(key);
//如果不存在
if (node == null) {
DoubleLinkedList newNode = new DoubleLinkedList(key, value);
//移动到头节点
addHead(newNode);
map.put(key,newNode);
size++;
//如果容量满了
if (size > capacity) {
//删除末尾节点接就是最久未使用的
DoubleLinkedList lastNode = removeLastNode();
map.remove(lastNode.key);
size--;
}
}else {
//更新value值
node.value = value;
moveToHead(node);
}
}
//删除末尾节点
private DoubleLinkedList removeLastNode() {
DoubleLinkedList last = tail.prev;
removeNode(last);
return last;
}
//移动节点到头节点
private void moveToHead(DoubleLinkedList node) {
removeNode(node);
addHead(node);
}
//删除节点
private void removeNode(DoubleLinkedList node) {
node.next.prev = node.prev;
node.prev.next = node.next;
node.next = null;
node.prev = null;
}
//添加节点(默认都是添加到头节点)
private void addHead(DoubleLinkedList node) {
node.next = head.next;
node.prev = head;
head.next.prev = node;
head.next = node;
}
}
❤最后
我是 Code皮皮虾,一个热爱分享知识的 皮皮虾爱好者,未来的日子里会不断更新出对大家有益的博文,期待大家的关注!!!
创作不易,如果这篇博文对各位有帮助,希望各位小伙伴可以一键三连哦!,感谢支持,我们下次再见~~~,不,我们天天见😊
欢迎各位小伙伴们关注我的公众号:JavaCodes,名称虽带Java但涉及范围可不止Java领域噢😁,会长期分享博文或者福利,期待您的关注❤