LRU,Least Recently Used,即最近最少使用,LRU缓存是经常使用的缓存淘汰策略之一。
我们可以使用LinkedHashMap的特性很容易实现LRU缓存。
1、访问排序
LinkedHashMap的构造方法中,有个accessorder参数,传true时,在调用get方法获取值时会对获取的值进行排序:
afterNodeAccess方法会将节点移至链表尾部:
afterNodeAccess方法实际是HashMap提供的扩展点,子类可以去重写这些扩展点来实现节点插入、访问和插入时额外的操作,上述代码既是LinkedHashMap的实现。
2、removeEldestEntry方法
该方法默认返回false,此方法是在插入节点后调用
我们可以重写removeEldestEntry方法,当插入元素个数大于链表size时,返回true,此时就会移除表头元素。
基于以上2个特性,编写LruCache代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| public class LruCache { private Map<Object, Object> cacheMap; private int size = 5; public LruCache() { cacheMap = new LinkedHashMap<Object, Object>(size, .75f, true) {
@Override protected boolean removeEldestEntry(Map.Entry<Object, Object> eldest) { return size() > size; } }; } public Object get(Object key) { return cacheMap.get(key); } public void put(Object key, Object value) { cacheMap.put(key, value); } public void remove(Object key) { cacheMap.remove(key); } public void clear() { cacheMap.clear(); } @Override public String toString() { StringBuilder sb = new StringBuilder(); cacheMap.forEach((k, v)-> { sb.append("(").append(k).append(",").append(v).append(")"); }); return sb.toString(); } public static void main(String[] args) { LruCache cache = new LruCache(); for (int i = 0; i < 10; i++) { cache.put(i, i); cache.get(0); System.out.println(cache); } } }
|
运行结果如下:
可以看出,我们一直get(0), (0,0)一直排在表尾,当缓存超过设定size时,表首元素会被淘汰。
mybatis中使用lru缓存也是基于LinkedHashMap去实现的,大家有兴趣可以了解下。