如何使用LinkedHashMap来实现一个LruCache

最近在看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;
      }
    };
  }
相关文章
相关标签/搜索