目录
1 LinkedHashMap有序
2 LRU机制介绍
3 LinkedHashMap原理
3.1 put方法
3.2 get方法
LinkedHashMap是有序的原因是LinkedHashMap的内部了Entry在继承了HashMap.Node基础上新增了前驱和后继属性。
static class Entry<K,V> extends HashMap.Node<K,V> { Entry<K,V> before, after; Entry(int hash, K key, V value, Node<K,V> next) { super(hash, key, value, next); } }LRU就是基于这个特性实现的
何为LRU?基Least Recent Used,最近最少访问,通常用于缓存维护。当缓存服务器内存或别的资源不足时,把那些不经常访问的缓存数据给删除掉。 我们看一下构造方法
public LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor); this.accessOrder = accessOrder; }这里重点关注第三个参数accessOrder,默认false,遍历元素的时候按照插入顺序;如果为true则按照访问顺序。下面简单实现衣蛾LRU缓存
public class LruCache { private LinkedHashMap<String, String> map; private int cacheSize; public LruCache(int cacheSize) { this.cacheSize = cacheSize; /** * 第三个参数accessOrder,默认false,遍历元素的时候按照插入顺序;如果为true则按照访问顺序 */ map = new LinkedHashMap<String, String>(cacheSize, 0.75F, true) { private static final long serialVersionUID = -6650855993931928707L; /** * 当map中的元素大于阈值时移除最老的元素 * @param eldest * @return */ @Override protected boolean removeEldestEntry(Map.Entry<String, String> eldest) { boolean result = size() > cacheSize; if (result) { System.out.println("remove eldest entry:" + eldest); } return result; } }; } public void put(String k, String v) { map.put(k, v); } public String get(String k) { return map.get(k); } public void print() { System.out.println(map); } }测试代码:
public static void main(String[] args) { LruCache lruCache = new LruCache(5); for (int i = 0; i < 5; i ++) { lruCache.put(i + "", i + ""); } lruCache.print(); lruCache.put("6", "6"); lruCache.print(); System.out.println("get key 1"); lruCache.get("1"); lruCache.print(); lruCache.put("7", "7"); lruCache.print(); }输出: {0=0, 1=1, 2=2, 3=3, 4=4} remove eldest entry:0=0 {1=1, 2=2, 3=3, 4=4, 6=6} get key 1 {2=2, 3=3, 4=4, 6=6, 1=1} remove eldest entry:2=2 {3=3, 4=4, 6=6, 1=1, 7=7} 可以看到添加缓存元素6后会把最先添加的缓存元素0给移除掉。 访问缓存元素1后会把1放到末尾。这样就实现了LRU策略缓存
put方法是继承的父类HashMap方法,每次添加元素后会执行模板方法afterNodeInsertion
// put方法evict传的是true void afterNodeInsertion(boolean evict) { // possibly remove eldest LinkedHashMap.Entry<K,V> first; if (evict && (first = head) != null && removeEldestEntry(first)) { // 如果头结点不为空并且需要移除头部元素的时候进行移除 K key = first.key; removeNode(hash(key), key, null, false, true); } }LinkedHashMap中removeEldestEntry方法返回的是false,默认不删除最老的元素,但是我们重写了该方法当容量超过阈值时移除头部元素
这样就实现了链表头部是最早访问的元素,缓存淘汰的时候优先淘汰头部元素