LinkedHashMap实现LRU缓存算法
缓存这个东西就是为了提高运行速度的,由于缓存是在寸土寸金的内存里面,不是在硬盘里面,所以容量是很有限的。
LRU这个算法就是把最近一次使用时间离现在时间最远的数据删除掉。
先说说List:每次访问一个元素后把这个元素放在 List一端,这样一来最远使用的元素自然就被放到List的另一端。缓存满了t的时候就把那最远使用的元素remove掉。但更实用的是 HashMap。因为List太慢,要删掉的数据总是位于List底层数组的第一个位置,删掉之后,后面的数据要向前补位。。所以复杂度是O(n),那就用链表结构的LinkedHashMap呗~,LinkedHashMap默认的元素顺序是put的顺序,但是如果使用带参数的构造函数,那么 LinkedHashMap会根据访问顺序来调整内部 顺序。
LinkedHashMap的get()方法除了返回元素之外还可以把被访问的元素放到链表的底端,这样一来每次顶端的元素就是remove的元素。
构造函数如下:
public LinkedHashMap (int initialCapacity, float loadFactor, boolean accessOrder);
- 1
- 2
initialCapacity 初始容量
loadFactor 加载因子,一般是 0.75f
accessOrder false 基于插入顺序 true 基于访问顺序(get一个元素后,这个元素被加到最后,使用了LRU 最近最少被使用的调度算法)
看一下LinkedHashMap的相关实现源码吧:
// 双向链表的 head节点
private transient Entry<K,V> header;// 访问顺序 true:访问顺序;false:插入顺序
private final boolean accessOrder;// get方法 每个get之后,都要进行recordAccess操作
public V get(Object key) {Entry<K,V> e = (Entry<K,V>)getEntry(key);if (e == null)return null;e.recordAccess(this);return e.value;
}// recordAccess 方法中,先remove该节点,再在head之间添加节点,表示最近访问了
void recordAccess(HashMap<K,V> m) {LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;if (lm.accessOrder) {lm.modCount++;remove();addBefore(lm.header);}
}// 删除该节点
private void remove() {before.after = after;after.before = before;
}private void addBefore(Entry<K,V> existingEntry) {after = existingEntry;before = existingEntry.before;before.after = this;after.before = this;
}// 添加新的entry时,如果需要删除eldest节点,就会删除head.after,如果是访问顺序,也就是最早以前访问的节点
void addEntry(int hash, K key, V value, int bucketIndex) {super.addEntry(hash, key, value, bucketIndex);// Remove eldest entry if instructedEntry<K,V> eldest = header.after;if (removeEldestEntry(eldest)) {removeEntryForKey(eldest.key);}
} // 默认的removeEldestEntry方法是返回false的,也就是不会进行删除,而是进行扩容protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {return false;
}
这是实现LRU的关键,我们可以重写这个方法,让其删除eldest entry;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
来个例子吧:
import java.util.*;
class Test
{ public static void main(String[] args) throws Exception{ Map<Integer,Integer> map=new LinkedHashMap<>(10,0.75f,true); map.put(9,3); map.put(7,4); map.put(5,9); map.put(3,4); //现在遍历的话顺序肯定是9,7,5,3 //下面访问了一下9,3这个键值对,输出顺序就变喽~ map.get(9); for(Iterator<Map.Entry<Integer,Integer>> it=map.entrySet().iterator();it.hasNext();){ System.out.println(it.next().getKey()); } }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
输出
7
5
3
9
好玩吧~
下面开始实现LRU缓存喽~
import java.util.*;
//扩展一下LinkedHashMap这个类,让他实现LRU算法
class LRULinkedHashMap<K,V> extends LinkedHashMap<K,V>{ //定义缓存的容量 private int capacity; private static final long serialVersionUID = 1L; //带参数的构造器 LRULinkedHashMap(int capacity){ //调用LinkedHashMap的构造器,传入以下参数 super(16,0.75f,true); //传入指定的缓存最大容量 this.capacity=capacity; } //实现LRU的关键方法,如果map里面的元素个数大于了缓存最大容量,则删除链表的顶端元素 @Override public boolean removeEldestEntry(Map.Entry<K, V> eldest){ System.out.println(eldest.getKey() + "=" + eldest.getValue()); return size()>capacity; }
}
//测试类
class Test{
public static void main(String[] args) throws Exception{ //指定缓存最大容量为4 Map<Integer,Integer> map=new LRULinkedHashMap<>(4); map.put(9,3); map.put(7,4); map.put(5,9); map.put(3,4); map.put(6,6); //总共put了5个元素,超过了指定的缓存最大容量 //遍历结果 for(Iterator<Map.Entry<Integer,Integer>> it=map.entrySet().iterator();it.hasNext();){ System.out.println(it.next().getKey()); } }
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
输出结果如下
9=3
9=3
9=3
9=3
9=3
7
5
3
分析一下:使用带参数构造器,且启用LRU模式的LinkedHashMap会在每次有新元素加入的时候,判断当前储存元素是否超过了缓存上限,也就是执行
一次removeEldestEntry方法,看最后的遍历结果,发现果然把9删除了,LRU发挥作用了~
LinkedHashMap实现LRU缓存算法相关推荐
- 实现 LRU 缓存算法
1 LRU 缓存介绍 LRU 算法全称是最近最少使用算法(Least Recently Use),是一种简单的缓存策略.顾名思义,LRU 算法会选出最近最少使用的数据进行淘汰. 那么什么是缓存呢?缓存 ...
- 保研面试 算法题_面试挂在了一道 LRU 缓存算法设计题
前言 好吧,有人可能觉得我标题党了,但我想告诉你们的是,前阵子面试确实挂在了 RLU 缓存算法的设计上了. 当时面试官问到这个题的时候,很快给了一个思路,但是手写的时候,发现自己没有深刻理解它,加上当 ...
- 【文科生带你读JavaScript数据结构与算法】2. 双向链表与LRU缓存算法原理与实现(下)
上篇聊了聊双向链表(doubly linked list)这个数据结构,今天就来点更实际的,也可以用双链表来实现的一种生活工作中不可或缺的优化算法--LRU缓存(Least Recently Used ...
- java mysql lru_Java集合详解5:深入理解LinkedHashMap和LRU缓存
今天我们来深入探索一下LinkedHashMap的底层原理,并且使用linkedhashmap来实现LRU缓存. 摘要:HashMap和双向链表合二为一即是LinkedHashMap.所谓Linked ...
- Java集合详解5:深入理解LinkedHashMap和LRU缓存
<Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查 ...
- LRU缓存算法缓存设计和实现
什么是缓存? 举个例子,去图书馆查资料,一般情况下我们会集中把我们有可能查阅的几本书从书架取下来,放在我们的桌面上,以便交叉查阅,从而避免频繁的从座位上跑到书架旁去取书.在这个例子里,书桌所扮演的就是 ...
- JDK1.8源码分析:LinkedHashMap与LRU缓存设计思路
概述 LinkedHashMap继承于HashMap,在HashMap的基础上,新增了两个特性: 支持以节点的插入顺序来迭代该map内的所有节点: 支持缓存设计中LRU的特性,即LinkedHashM ...
- 算法必知 --- LRU缓存淘汰算法
作者:_code_x 链接:https://www.jianshu.com/p/b7fed77324b9 写在前 就是一种缓存淘汰策略. 计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾 ...
- LRU缓存实现(Java)
LRU是Least Recently Used 的缩写,翻译过来就是"最近最少使用",LRU缓存就是使用这种原理实现,简单的说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的 ...
最新文章
- javascript-对混合字母/数字数组进行排序
- Windows 家族的十二种常用密码破解法
- Android中measure过程、WRAP_CONTENT详解以及xml布局文件解析流程浅析(下)
- windows10 ipv4设置两个(多个)网段同时连接(多网段、双网段)
- python 列表创建
- github搭建个人博客 hexo d无效
- 在容器内源码安装PostgreSQL13.2
- mysql分裂函数函数_分裂函数等价于T-SQL?
- Spring的三种注入方式
- live2d_基于Qt的Live2D示例(几乎都是官方给的代码就是啦!)
- 企业文化是数字化转型最大障碍-解读《2022年首席数据官调查报告》
- 海贼oj#130. 计算复利2
- vue导出excel加一个进度条_vue项目中如何把数据导出成excel文件
- windows添加右键打开cmd命令窗口
- terracotta安装配置与集群过程
- PHP语言编程魔方,php魔方方法总结
- dmz主机就是DNAT功能的体现
- word标尺灰色_如何在Microsoft Word中显示和隐藏标尺
- 软件单元测试操作步骤(java版)
- 寒假社会实践完整版内容,一套流程
热门文章
- c语言中常用的预处理命令6,C语言的预处理命令
- Linux配置最基础的命令
- 各大型邮箱smtp服务器及端口收集:
- docker 进入容器命令_Docker容器常用命令分享,赶紧收藏
- 2021年东港二中高考成绩查询,辽宁省东港二中2021届英语高考模拟试卷1(新课改原创2020版,供2021年课改省份考生使用)...
- android 获取位置数据库,尝试从webview获取位置时,Android“SQLite数据库无法从/CachedGeoposition.db加载”错误...
- gridreport如何设置打印3次_如何设置光固化3D打印机切片参数
- 通讯录排序 (20分)_算法入门篇:简单的排序算法
- python如何实现matlab_Python实现matlab数据绘制
- visio保存后公式变形_涨姿势了!仿真变形后的模型还能保存下来