1、思考
- 需要一个最大的缓存量
- put进去值得时候,是有顺序的,每次放在最前面
- get的时候查询很快,查完后放在最前,代表着优先级
- 当满的时候,删除在队尾的元素
2、需要有顺序,且查询快,则选择数据结构为LinkedHashMap(继承与HashMap),是双向链表和哈希表的结合体 结构如下图所示:
- 每次默认从链表尾部添加元素,越靠近尾部的就是最近使用的,越靠头部的元素就是最近未使用的
- 对于某一个key,我们可以通过哈希表快速定位到链表中的节点,从而取得对应的值
- 传统的来链表支持快速增删,但是无法按照索引访问每一个位置的元素,借助哈希表
为什么要是双向链表,单链表行不行?另外,既然哈希表中已经存了
key,为什么链表中还要存key和val呢,只存val不就行了?
删除一个节点不光要得到该节点本身的指针,也需要操作其前驱节点的指针,而双向链表才能支持直接查找前驱,保证操作的时间复杂度 O(1)。
代码
class LRUCache {
int cap;
LinkedHashMap<Integer, Integer> cache = new LinkedHashMap<>();
//进行初始化
public LRUCache(int capacity) {
this.cap = capacity;
}
//获得逻辑 若他是之前已经得到的,把之前的key,变为最近使用
//若不包含,返回-1
public int get(int key) {
if (!cache.containsKey(key)) {
return -1;
}
// 将 key 变为最近使用
makeRecently(key);
return cache.get(key);
}
//新加入,包含则修改key值,将key变为最近使用的
//否则。则判断是否缓存已经满了,满了,则删除链表头部的元素,进行删除
//调用迭代器,一直迭代到最前面
//没满,则直接加入
public void put(int key, int val) {
if (cache.containsKey(key)) {
// 修改 key 的值
cache.put(key, val);
// 将 key 变为最近使用
makeRecently(key);
return;
}
if (cache.size() >= this.cap) {
// 链表头部就是最久未使用的 key
int oldestKey = cache.keySet().iterator().next();
cache.remove(oldestKey);
}
// 将新的 key 添加链表尾部
cache.put(key, val);
}
//最近使用:得到之前的,将之前的进行删除,再重新加到队尾中
private void makeRecently(int key) {
int val = cache.get(key);
// 删除 key,重新插入到队尾
cache.remove(key);
//将新的对应的值进行插入
cache.put(key, val);
}
}
总结:
- 什么情况下才会提高优先级? 刚访问过,具有重复的,新添加的
- 提高优先级的方法定义
- 缓存使用linkedHashMap来实现
优先级方法的定义
//优先级的提高不包活缓存中没有的,在map尾部就是优先级最高的
public void makeRecently(int key){
int value = cache.get(key);
cache.remove(key);
cache.put(key,value);
}
刚访问一个元素更新优先级
public int get(int key){
if(!cache.containsKey(key)){
return -1;
}
makeRecently(key);
return cache.get(key);
}
添加元素
public void put(int key,int value){
if(cache.containsKey(key)){
makeRencently(key); //只能提升优先级
cache.put(key,value); //他还是要进行put的
return;
}
//动 > 静
if(cache.size()>=cap){
//找出最前面的那个
int first = cache.keySet().iterator().next();
cache.remove(first);
}
cache.put(key,value);
}