Java 自定義實現 LRU 快取演算法

codeceo發表於2015-07-28

背景

LinkedHashMap繼承自HashMap,內部提供了一個removeEldestEntry方法,該方法正是實現LRU策略的關鍵所在,且HashMap內部專門為LinkedHashMap提供了3個專用回撥方法,afterNodeAccess、afterNodeInsertion、afterNodeRemoval,這3個方法的字面意思非常容易理解,就是節點訪問後、節點插入後、節點刪除後分別執行的行為。基於以上行為LinkedHashMap就可以實現一個LRUCache的功能了。

關於LinkedHashMap的eldest:eldest字面意思為最老的,LinkedHashMap中有個叫做accessOrder的欄位,當accessOrder為true時表示LinkedHashMap內部節點按照訪問次數排序,最老的節點也就是訪問最少的節點。當accessOrder為false時表示LinkedHashMap內部節點按照插入順序排序,最老的節點也就是最早插入的節點,該值預設為false。

實現

自己實現LRUCache只需覆蓋removeEldestEntry這個方法即可,程式碼如下

private static class LRUCache<K, V> extends LinkedHashMap<K, V>
	{
		private static final long serialVersionUID = -9111855653176630846L;
		private static int MAX_ELEMENTS;

		public LRUCache(int initCap, int maxSize) throws IllegalArgumentException
		{
			super(initCap, 0.75f, true);
			if (maxSize < 0)
				throw new IllegalArgumentException();
			MAX_ELEMENTS = maxSize;
		}

		@Override
		protected boolean removeEldestEntry(Map.Entry<K, V> eldest)
		{
			return size() > MAX_ELEMENTS;
		}
	}

以上程式碼需要一個MAX_ELEMENTS變數限制最大儲存節點個數,插入節點時判斷 如果當前節點個數已經超過了這個值則會根據LRU策略將訪問最少的那個節點刪除,這裡需要注意,預設LinkedHashMap保證的是插入順序,也就是節點按照插入先後來排序的,所以就算刪除也是刪除最先插入的節點,但是我們在建構函式中傳入了一個true,這個引數決定了LinkedHashMap內部的節點按照什麼方式排序,引數為true時說明內部節點按照最近訪問的時間排序,為false時說明按照插入順序排序。至此已完成了一個簡易的LRUCache實現。

注意

由於LinkedHahsMap本身實現不是執行緒安全的,也就是說這個LRUCache也不是執行緒安全的,如果想要能多執行緒訪問的話,可以這樣使用它:LRUCache cache = Collections.synchronizedMap(new LRUCache(10, 10))。這樣cache就可以在多執行緒下執行get/put等操作了,但是,用這種方式得到的cache在多執行緒遍歷時還是不安全的。所以不能在多執行緒下遍歷cache,官方文件也建議在遍歷synchronizedmap時使用map本身做同步。

相關文章