如何使用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; } }; }
本文转载自:https://www.cnblogs.com/jiaoyiping/p/10604463.html