1. 概述
LRU 緩存介紹
LRU 是 Least Recently Used 的縮寫,翻譯過來就是「最近最少使用」,也就是說,LRU 緩存把最近最少使用的數據移除,讓給最新讀取的數據。而往往最常讀取的,也是讀取次數最多的,所以,利用 LRU 緩存,我們能夠提高系統的性能。
實現
要實現 LRU 緩存,我們首先要用到一個類 LinkedHashMap。
用這個類有兩大好處:一是它本身已經實現了按照訪問順序的存儲,也就是說,最近讀取的會放在最前面,最最不常讀取的會放在最後(當然,它也可以實現按照插入順序存儲)。第二,LinkedHashMap 本身有一個方法用於判斷是否需要移除最不常讀取的數,但是,原始方法默認不需要移除(這是,LinkedHashMap 相當於一個linkedlist),所以,我們需要 override 這樣一個方法,使得當緩存裏存放的數據個數超過規定個數後,就把最不常用的移除掉。
在介紹的HashMap後,我們來學習LinkedHashMap的工作原理及實現。首先還是類似的,我們寫一個簡單的LinkedHashMap的程序:
1
2
3
4
5
6
7
8
9
10
11
12
|
LinkedHashMap<String, Integer> lmap =
new
LinkedHashMap<String, Integer>();
lmap.put(
"語文"
,
1
);
lmap.put(
"數學"
,
2
);
lmap.put(
"英語"
,
3
);
lmap.put(
"歷史"
,
4
);
lmap.put(
"政治"
,
5
);
lmap.put(
"地理"
,
6
);
lmap.put(
"生物"
,
7
);
lmap.put(
"化學"
,
8
);
for
(Entry<String, Integer> entry : lmap.entrySet()) {
System.out.println(entry.getKey() +
": "
+ entry.getValue());
}
|
運行結果是:
1
2
3
4
5
6
7
8
|
語文:
1
數學:
2
英語:
3
歷史:
4
政治:
5
地理:
6
生物:
7
化學:
8
|
我們可以觀察到,和HashMap的運行結果不同,LinkedHashMap的迭代輸出的結果保持了插入順序。是什麼樣的結構使得LinkedHashMap具有如此特性呢?我們還是一樣的看看LinkedHashMap的內部結構,對它有一個感性的認識。
沒錯,正如官方文檔所說:
Hash table and linked list implementation of the Map interface, with predictable iteration order. This implementation differs from HashMap in that it maintains a doubly-linked listrunning through all of its entries. This linked list defines the iteration ordering, which is normally the order in which keys were inserted into the map (insertion-order).
LinkedHashMap是Hash表和鏈表的實現,並且依靠着雙向鏈表保證了迭代順序是插入的順序。
2. 三個重點實現的函數
在HashMap中提到了下面的定義:
1
2
3
4
|
// Callbacks to allow LinkedHashMap post-actions
void
afterNodeAccess(Node<K,V> p) { }
void
afterNodeInsertion(
boolean
evict) { }
void
afterNodeRemoval(Node<K,V> p) { }
|
LinkedHashMap繼承於HashMap,因此也重新實現了這3個函數,顧名思義這三個函數的作用分別是:節點訪問後、節點插入後、節點移除後做一些事情。
afterNodeAccess函數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
void
afterNodeAccess(Node<K,V> e) {
// move node to last
LinkedHashMap.Entry<K,V> last;
// 如果定義了accessOrder,那麼就保證最近訪問節點放到最後
if
(accessOrder && (last = tail) != e) {
LinkedHashMap.Entry<K,V> p =
(LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
p.after =
null
;
if
(b ==
null
)
head = a;
else
b.after = a;
if
(a !=
null
)
a.before = b;
else
last = b;
if
(last ==
null
)
head = p;
else
{
p.before = last;
last.after = p;
}
tail = p;
++modCount;
}
}
|
就是說在進行put之後就算是對節點的訪問了,那麼這個時候就會更新鏈表,把最近訪問的放到最後,保證鏈表。
afterNodeInsertion函數
1
2
3
4
5
6
7
8
|
void
afterNodeInsertion(
boolean
evict) {
// possibly remove eldest
LinkedHashMap.Entry<K,V> first;
// 如果定義了移除�ight:auto!important; top:auto!important; vertical-align:baseline!important; width:auto!important; min-height:auto!important; color:black!important; background:none!important"> afterNodeInsertion(
boolean
evict) {
// possibly remove eldest
LinkedHashMap.Entry<K,V> first;
// 如果定義了移除規則,則執行相應的溢出
if
(evict && (first = head) !=
null
&& removeEldestEntry(first)) {
|