最近在看mybatis的源代码,发现了mybatis中实现的LruCache使用到了LinkedHashMap,因此就探究了一下LinkedHashMap是如何支持Lru缓存的java
LinkedHashMap内部维护了一个全部的Entity的双向链表缓存
同时构造方法能够设置Iterator的时候,是按照插入的顺序排序仍是按照访问的顺序排序mybatis
默认是按照插入的顺序来排序的,在构造方法里边能够设置按照访问的顺序来排序ide
那究竟按照访问的顺序来排序是什么意思呢?this
LinkedHashMap的get(key)方法是本身实现的,并无从HashMap里边继承,咱们看看get(Key)方法的实现是什么样子的
3d
咱们看afterNodeAccess()方法是如何实现的指针
这个方法主要就是移动双向链表的指针,将传入的结点移动到LinkedHashMap维护的双向链表的末尾,这样每次经过get(key)方法访问一个元素,这个元素就会被移动到双向链表的末尾,按照访问的顺序来排序,就是每次经过Iterator来遍历keySet或者是EntrySet的时候,访问过的元素会出如今最后边(由于LinedHashMap的Iterator遍历的时候,遍历的是内部的双向链表,从头结点,遍历到尾结点)code
顺着这样的思路,若是在知足必定条件的状况下,移除掉双向链表的头结点,这样就实现了一个LruCaheblog
其实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; } }; }