最近在看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的更多相关文章

  1. 通过反射,获取linkedHashMap的最后一个键值对。对map按照值进行排序。

    1:通过反射,获取linkedHashMap的最后一个键值对. Map<Integer, Integer> map = new LinkedHashMap<>(); Field ...

  2. 使用LinkedHashMap来实现一个使用LRU(Least Recently Used)算法的cache

    removeEldestEntry在使用put或者putAll方法插入一个新的entry到map中时被调用,是否要删除年老的entry取决于是否满足既定的条件(比如本例中的条件:MAP中entry数量 ...

  3. 小心LinkedHashMap的get()方法(转)

    这是一个来自实际项目的例子,在这个案例中,有同事基于jdk中的LinkedHashMap设计了一个LRUCache,为了提高性能,使用了 ReentrantReadWriteLock 读写锁:写锁对应 ...

  4. Java 自定义实现 LRU 缓存算法

    背景 LinkedHashMap继承自HashMap,内部提供了一个removeEldestEntry方法,该方法正是实现LRU策略的关键所在,且HashMap内部专门为LinkedHashMap提供 ...

  5. LinkedHashMap 与 LRUcache

    LRU 缓存介绍 我们平时总会有一个电话本记录所有朋友的电话,但是,如果有朋友经常联系,那些朋友的电话号码不用翻电话本我们也能记住,但是,如果长时间没有联系了,要再次联系那位朋友的时候,我们又不得不求 ...

  6. Android开发学习之路-LruCache使用和源码分析

    LruCache的Lru指的是LeastRecentlyUsed,也就是近期最少使用算法.也就是说,当我们进行缓存的时候,如果缓存满了,会先淘汰使用的最少的缓存对象. 为什么要用LruCache?其实 ...

  7. 一起写一个Android图片加载框架

    本文会从内部原理到具体实现来详细介绍如何开发一个简洁而实用的Android图片加载缓存框架,并在内存占用与加载图片所需时间这两个方面与主流图片加载框架之一Universal Image Loader做 ...

  8. android之LruCache源代码解析

    移动设备开发中,因为移动设备(手机等)的内存有限,所以使用有效的缓存技术是必要的.android提供来一个缓存工具类LruCache,开发中我们会经经常使用到,以下来他是怎样实现的. 在package ...

  9. LinkedHashMap相关信息介绍(转)

    Java中的LinkedHashMap此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表.此链接列表定义了迭代顺序,该迭代顺序通常就是将键插入到映射中的顺序(插入顺序 ...

随机推荐

  1. JS的document.links函数使用示例

    ? <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title&g ...

  2. GMA Round 1 双曲线与面积

    传送门 双曲线与面积 P是双曲线$\frac{x^2}{1471^2}-\frac{y^2}{1372^2}=1$上的一个动点,现在过P作一条直线与该双曲线的两条渐近线相交于A.B两点,且|AP|=| ...

  3. hihocoder1323 回文字符串(区间dp)

    https://hihocoder.com/problemset/problem/1323 刚开始真没看出来这是一道dp题.. dp[i][j]表示i~j段修改成回文串所需的最少操作次数.然后根据s[ ...

  4. C#隐藏手机号中间四位为*

    使用正则:Regex.Replace(手机号, "(\\d{3})\\d{4}(\\d{4})", "$1****$2"); 效果:

  5. Creating a NuGet Package in 7 easy steps - Plus using NuGet to integrate ASP.NET MVC 3 into existing Web Forms applications

    UPDATE: Check out my follow up post where I remove the need for editing the Global.asax.cs and show ...

  6. 2.1 mac下多版本jdk的安装和管理

    之前已经安装过jdk8了,安装路径:/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk 现在安装jdk10,下载后,双击dmg文件一直到安装完成,安装 ...

  7. 分布式环境中,模块数据交互协议分析 (百度brpc)

    1. 背景 之前听到同事说,要为自己的模块考虑写个数据协议.今天有空想了一下.写出来,方便后续使用. 开源代码brpc中可以支持多种协议,nshead.redis.mongo等20多种协议. 2. 什 ...

  8. 【Zuul】Zuul过滤器参考资料

    #https://blog.csdn.net/chenqipc/article/details/53322830#https://github.com/spring-cloud/spring-clou ...

  9. 资源贴——以备时时查询用

    目录区 AI教程   AI教程 1.AI教程!教你绘制小清新巴士   2.AI教程!如何使用基础图形来绘制消防插画   3.AI教程!教你绘制秋日插画   4.AI教程!教你制作色彩分明的街边场景插画 ...

  10. mac ssh 连接超时

    打开终端,进入,/etc/ssh.sudo vi ssh_config 最后一行添加 ServerAliveInterval30 然后保存就可以了