使用LinkedHashMap實現Cache的方法與原理
基於LinkedHashMap的特性,可以實現一個簡單的基於LRU算法的緩存功能。
首先大致介紹一下LinkedHashMap的特點。
通過LinkedHashMap的簽名我們可以看出 LinkedHashMap是HashMap的一個子類,它根據自身的特點修改了某些某類的方法
在這裏八卦一下這個簽名,我們可以看到LinkedHashMap繼承了HashMap並實現了Map接口。
我們再來看一下HashMap的簽名,HashMap繼承了AbstractMap 並實現了Map接口,
我們再來看一下AbstractMap的簽名
AbstractMap本身也實現了Map接口
但爲什麼HashMap和LinkedHashMap還要實現這個接口呢
在stackoverflow.網站找到了一個類似的帖子,只不過帖子問的是有關於WeakHashMap這個類的,但其道理是一樣的
回覆如下
大概意思是說只是爲了生成javadoc會比較良好,其實在語法上是不需要的。
設計原理
LinkedHashMap我們都知道是基於雙向鏈表的,如下圖
但LinkedHashMap代源碼中我們可以看出,他不僅是一個雙向鏈表,而且是一個環型的雙向鏈表
我們在源碼可以看到LinkedHashMap的init方法覆蓋了父類的方法
然而在HashMap類中init方法僅僅是一個空實現,爲子類提供了一個勾子的作用
訪問順序
LinkedHashMap提供了兩種Key的順序
<!--[if !supportLists]-->l <!--[endif]-->訪問順序
<!--[if !supportLists]-->l <!--[endif]-->插入順序
LinkedHashMap默認是插入順序,這點我們在它的構造函數中可以看到
accessOrder就是訪問順序的意思,默認爲false,即使用插入順序
使用訪問順序的好處是,當我們使用get方法訪問元素的時候,在訪問過後,LinkedHashMap會將get的元素移到鏈表的尾部,這樣鏈表的第一個元素總是最早被放入到鏈表中並且沒有被訪問過的元素,正好符合我們的LRU原則。
通過源碼我們可以看到
LinkedHashMap並沒有重寫父類的put方法,
但是LinkedHashMap重新了父類put方法中的子方法addEntry方法
在addEntry方法中,我們看到了有一段代碼,是否刪除最老的一個元素。
按此推理,我們在實現緩存功能的時候,就需要重寫removeEldestEntry方法,在添加元素的時候判斷是否超過了緩存定義的容量。
按此方法,我在本地實現了一個簡單的cache功能,代碼如下
就這麼簡單。
測試代碼如下:
1. 元素大小小於緩存容量
運行結果
2. 元素大小大於緩存容量
運行結果
3. 元素大小大於緩存容量,在緩存容量滿之前對緩存數據進行訪問
運行結果