最近在看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; } }; }