LruCache,顾名思义,是一种具有 LRU 策略的缓存实现类。除此之外,MyBatis 还提
供了具有 FIFO 策略的缓存 FifoCache。不过并未提供 LFU 缓存
源代码:
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 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| public class LruCache implements Cache { private final Cache delegate; private Map<Object, Object> keyMap; private Object eldestKey; public LruCache(Cache delegate) { this.delegate = delegate; setSize(1024); } public int getSize() { return delegate.getSize(); } public void setSize(final int size) { // 初始化 keyMap,注意,keyMap 的类型继承自 LinkedHashMap, // 并覆盖了 removeEldestEntry 方法 keyMap = new LinkedHashMap<Object, Object>(size, .75F, true) { // 覆盖 LinkedHashMap 的 removeEldestEntry 方法 @Override protected boolean removeEldestEntry( Map.Entry<Object, Object> eldest) { boolean tooBig = size() > size; if (tooBig) { // 获取将要被移除缓存项的键值 eldestKey = eldest.getKey(); } return tooBig; } }; } @Override public void putObject(Object key, Object value) { // 存储缓存项 delegate.putObject(key, value); cycleKeyList(key); } @Override public Object getObject(Object key) { // 刷新 key 在 keyMap 中的位置 keyMap.get(key); // 从被装饰类中获取相应缓存项 return delegate.getObject(key); } @Override public Object removeObject(Object key) { // 从被装饰类中移除相应的缓存项 return delegate.removeObject(key); } @Override public void clear() { delegate.clear(); keyMap.clear(); } private void cycleKeyList(Object key) { // 存储 key 到 keyMap 中 keyMap.put(key, key); if (eldestKey != null) { // 从被装饰类中移除相应的缓存项 delegate.removeObject(eldestKey); eldestKey = null; } } }
|
LruCache 的 keyMap 属性是实现LRU策略的关键,该属性类型继承自LinkedHashMap,并覆盖了removeEldestEntry方法
LinkedHashMap 可保持键值对的插入顺序,当插入一个新的键值对时,LinkedHashMap 内部的 tail 节点会指向最新插入的节点。head节点则指向第一个被插入的键值对,也就是最久未被访问的那个键值对。
默认情况下,LinkedHashMap 仅维护键值对的插入顺序。若要基于 LinkedHashMap 实现 LRU 缓存,还需通过构造方法将 LinkedHashMap 的 accessOrder 属性设为 true,此时 LinkedHashMap 会维护键值对的访问顺序。
上面代码中 getObject 方法中执行了这样一句代码keyMap.get(key) ,目的是刷新key 对应的键值对在 LinkedHashMap的位置。LinkedHashMap会将key对应的键值对移动到链表的尾部,尾部节点表示最久刚被访问过或者插入的节点。
除了需将 accessOrder 设为 true,还需覆盖removeEldestEntry 方法。LinkedHashMap 在插入新的键值对时会调用该方法,以决定是否在插入新的键值对后,移除老的键值对。
当被装饰类的容量超出了keyMap的所规定的容量(由构造方法传入)后,keyMap会移除最长时间未被访问的键,并将该键保存到eldestKey中,然后由cycleKeyList方法将eldestKey传给被装饰类的removeObject方法,移除相应的缓存项目。