如何使用LinkedHashMap来实现一个LruCache
最近在看mybatis的源代码,发现了mybatis中实现的LruCache使用到了LinkedHashMap,所以就探究了一下LinkedHashMap是如何支持Lru缓存的
LinkedHashMap内部维护了一个所有的Entity的双向链表
同时构造方法可以设置Iterator的时候,是按照插入的顺序排序还是按照访问的顺序排序

默认是按照插入的顺序来排序的,在构造方法里边可以设置按照访问的顺序来排序

那究竟按照访问的顺序来排序是什么意思呢?
LinkedHashMap的get(key)方法是自己实现的,并没有从HashMap里边继承,我们看看get(Key)方法的实现是什么样子的

我们看afterNodeAccess()方法是如何实现的

这个方法主要就是移动双向链表的指针,将传入的结点移动到LinkedHashMap维护的双向链表的末尾,这样每次通过get(key)方法访问一个元素,这个元素就会被移动到双向链表的末尾,按照访问的顺序来排序,就是每次通过Iterator来遍历keySet或者是EntrySet的时候,访问过的元素会出现在最后边(因为LinedHashMap的Iterator遍历的时候,遍历的是内部的双向链表,从头结点,遍历到尾结点)
顺着这样的思路,如果在满足一定条件的情况下,移除掉双向链表的头结点,这样就实现了一个LruCahe
其实LinkedHashMap已经为我们提供了这样的方法,LinkedHashMap中有一个方法removeEldestEntry(entry) 我们只需要覆盖这个方法,根据我们自己的需求在一定条件下返回true,这样就实现了LruCache
改方法的默认实现是返回false
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
LinkedHashMap的afterNodeInsertion()方法会根据其他条件以及removeEldestEntry的返回值来决定是否删除到双向链表的表头元素

依据此,我们使用LinkedHashMap来实现一个最简单的Lru缓存如下:
import org.junit.Test;
import java.util.LinkedHashMap;
import java.util.Map;
public class TestCache {
@Test
public void testLinkedHashMap() {
LinkedHashMap<String, String> map = new LinkedHashMap<String, String>(5, 0.75F, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<String, String> eldest) {
//当LinkHashMap的容量大于等于5的时候,再插入就移除旧的元素
return this.size() >= 5;
}
};
map.put("aa", "bb");
map.put("cc", "dd");
map.put("ee", "ff");
map.put("gg", "hh");
print(map);
map.get("cc");
System.out.println("===================================");
print(map);
map.get("ee");
map.get("aa");
System.out.println("====================================");
map.put("ss","oo");
print(map);
}
void print(LinkedHashMap<String, String> source) {
source.keySet().iterator().forEachRemaining(System.out::println);
}
}
Mybatis中的Lrucache实现也是类似的思路,比较简单,下边是关键的代码:
构造方法中调用了setSize()方法,默认缓存1024个元素
public LruCache(Cache delegate) {
this.delegate = delegate;
setSize(1024);
}
setSize()方法中初始化了HashMap,并实现了removeEldestEntry()方法
public void setSize(final int size) {
keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) {
private static final long serialVersionUID = 4267176411845948333L;
@Override
protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) {
boolean tooBig = size() > size;
if (tooBig) {
eldestKey = eldest.getKey();
}
return tooBig;
}
};
}
如何使用LinkedHashMap来实现一个LruCache的更多相关文章
- 通过反射,获取linkedHashMap的最后一个键值对。对map按照值进行排序。
1:通过反射,获取linkedHashMap的最后一个键值对. Map<Integer, Integer> map = new LinkedHashMap<>(); Field ...
- 使用LinkedHashMap来实现一个使用LRU(Least Recently Used)算法的cache
removeEldestEntry在使用put或者putAll方法插入一个新的entry到map中时被调用,是否要删除年老的entry取决于是否满足既定的条件(比如本例中的条件:MAP中entry数量 ...
- 小心LinkedHashMap的get()方法(转)
这是一个来自实际项目的例子,在这个案例中,有同事基于jdk中的LinkedHashMap设计了一个LRUCache,为了提高性能,使用了 ReentrantReadWriteLock 读写锁:写锁对应 ...
- Java 自定义实现 LRU 缓存算法
背景 LinkedHashMap继承自HashMap,内部提供了一个removeEldestEntry方法,该方法正是实现LRU策略的关键所在,且HashMap内部专门为LinkedHashMap提供 ...
- LinkedHashMap 与 LRUcache
LRU 缓存介绍 我们平时总会有一个电话本记录所有朋友的电话,但是,如果有朋友经常联系,那些朋友的电话号码不用翻电话本我们也能记住,但是,如果长时间没有联系了,要再次联系那位朋友的时候,我们又不得不求 ...
- Android开发学习之路-LruCache使用和源码分析
LruCache的Lru指的是LeastRecentlyUsed,也就是近期最少使用算法.也就是说,当我们进行缓存的时候,如果缓存满了,会先淘汰使用的最少的缓存对象. 为什么要用LruCache?其实 ...
- 一起写一个Android图片加载框架
本文会从内部原理到具体实现来详细介绍如何开发一个简洁而实用的Android图片加载缓存框架,并在内存占用与加载图片所需时间这两个方面与主流图片加载框架之一Universal Image Loader做 ...
- android之LruCache源代码解析
移动设备开发中,因为移动设备(手机等)的内存有限,所以使用有效的缓存技术是必要的.android提供来一个缓存工具类LruCache,开发中我们会经经常使用到,以下来他是怎样实现的. 在package ...
- LinkedHashMap相关信息介绍(转)
Java中的LinkedHashMap此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表.此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序 ...
随机推荐
- SpringBoot无废话入门04:MyBatis整合
1.Parent引入及pom配置 首先,如果要支持mybatis,那么我们就应该引入mybatis的starter.同时,由于连接本身还需要用jdbc的connetor和连接池,所以一并需要引入这些依 ...
- rvs产生服从指定分布的随机数 pdf概率密度函数 cdf累计分布函数 ppf 分位点函数
统计工作中几个常用用法在python统计函数库scipy.stats的使用范例. 正态分布以正态分布的常见需求为例了解scipy.stats的基本使用方法. 1.生成服从指定分布的随机数 norm.r ...
- vue-cli配置多入口多出口,实现一个项目两个访问地址,区分不同上线环境
最近工作中需要把项目分割成两块,一块需要跑在微信中,通过微信jdk获取用户资料默认登录,一部分需要给原生app做webview的内嵌页面,当然这部分内容是不跑在微信中的. 所以我想到了把项目分成两部分 ...
- How to copy the contents of std::vector to c-style static array,safely?
[问题] I am getting warning when using the std copy function. I have a byte array that I declare. byte ...
- mysql 让id字段 以1000 形式开头
eg: alter table t_location AUTO_INCREMENT=1000;
- Deep Learning.ai学习笔记_第二门课_改善深层神经网络:超参数调试、正则化以及优化
目录 第一周(深度学习的实践层面) 第二周(优化算法) 第三周(超参数调试.Batch正则化和程序框架) 目标: 如何有效运作神经网络,内容涉及超参数调优,如何构建数据,以及如何确保优化算法快速运行, ...
- LIN 笔记
LIN 使用了 1 根线来进行通信,但是,它必须要参考 VBat 和 GND.离开这两个参考电平,并没有办法来判断线上的 bit 状态. 另外,根据经典的 LIN 驱动电路(一个 OC 门),RX 接 ...
- ROM后缀含义
ROM files, unless altered by the uploader, always have special suffixes to quickly denote what the s ...
- IntelliJ IDEA配置
1.取消idea默认打开工程: 2.导出,导入配置. 导出和导入配置好处:对IDEA配置好后导出配置,方便以后重装电脑或者重装IDEA后可以导入之前配置好的配置.避免重复配置 导出配置:File Ex ...
- pandas DataFrame apply()函数(2)
上一篇pandas DataFrame apply()函数(1)说了如何通过apply函数对DataFrame进行转换,得到一个新的DataFrame. 这篇介绍DataFrame apply()函数 ...