如何使用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 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表.此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序 ...
随机推荐
- oracle函数返回结果集
一.用自定义类型实现 1.创建表对象类型. 在Oracle中想要返回表对象,必须自定义一个表类型,如下所示: create or replace type type_table is table of ...
- python之名称空间
1 类名称空间 创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性 而类的良好总属性:数据属性和函数属性 其中类的数据属性是共享给所有对象 print(id(g1.c ...
- JS_高程3.基本概念(4)操作符
ECMA-262用于操作数据值的操作符包括: 算术操作符 位操作符 关系操作符 相等操作符 ECMAScript操作符的不同之处在于:它能够适用于很多值,包括字符串,数字值,布尔值,甚至是对象.(在应 ...
- python动态构建类(类似声明)
对于类实例的动态构建,那是非常的简单.可要在代码中动态的构建类,然后该类还能够被使用,那得多么的强大呀. 在Python中,内建的__builtin__提供了一个type的方法,用该方法可以动态的构建 ...
- c# zxing生成二维码和打印
生成二维码代码 asset=“要生成的字符串”: public static Bitmap CreateQRCode(string asset) { EncodingOptions options = ...
- 修改Arduino IDE默认字体
文件->首选项 点击直接编辑下面那个文件 修改editor.font这个条目就可以不用那么毁眼了..
- 提升SQLite数据插入效率低、速度慢的方法(转)
前言 SQLite数据库由于其简单.灵活.轻量.开源,已经被越来越多的被应用到中小型应用中.甚至有人说,SQLite完全可以用来取代C语言中的文件读写操作.因此我最近编写有关遥感数据处理的程序的时候, ...
- [Linux] - 利用ping给端口加密,限制访问
Linux中,想对特定的端口加密访问,可以使用iptables的ping方式. 作用 访问被限制的端口,必需先ping发送对应的字节包(字节包大小可自行设置,此为密钥)才能访问成功! 下边是对SSH的 ...
- iOS实现 webView loadHTMLString加载外部css、js样式
记录一下. webview(或wk)用 loadHTMLString加载内容时 ,如果只是单纯的html内容,样式等都写在内部,直接设置baseURL为nil即可. 不过当html里包含外部样式或调用 ...
- 分析轮子(五)- Vector.java
注:玩的是JDK1.7版本 一: 先上类图,从类图上看和 ArrayList.java 非常相像,可查看 分析轮子(一)-ArrayList.java 二:然后看源码,发现和 ArrayList.ja ...