方法一
完全使用Java的LinkedHashMap
來實現。實現也有兩種方法,一種使用繼承,另一種使用組合,這裏給出使用組合的代碼。
public class LRUCache<K, V> {
private Map<K, V> map;
private final int cacheSize;
public LRUCache(int initialCapacity) {
map = new LinkedHashMap<K, V>(initialCapacity, 0.75f, true) {
@Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > cacheSize;
}
};
this.cacheSize = initialCapacity;
}
}
核心部分在LinkedHashMap
的第三個構造函數上,要把這個構造參數accessOrder
設爲true,代表LinkedHashMap
內部維持訪問順序。另外,還需要重寫removeEldestEntry()
,這個函數如果返回true
,代表應該把最久未被訪問的節點移除。
這裏記錄一下LinkedHashMap
的三個方法:
void afterNodeAccess(Node<K,V> p) {}
void afterNodeInsertion(boolean evict) {}
void afterNodeRemoval(Node<K,V> p) {}
這三個方法在HashMap
中就有,而且爲空;LinkedHashMap
繼承了HashMap
,並重寫了這三個方法。下面是LinkedHashMap
中這三個函數的作用。
afterNodeAccess方法
作用:將目前訪問的節點放到鏈表最後。
調用位置:
- 在
get
方法中,如果accessOrder
設爲true
,調用該方法。 - 在
put
方法中,如果當前插入的值已經在Map
中,調用該方法。
afterNodeInsertion方法
作用:如果removeEldestEntry
函數返回true
,那麼該方法就把最久未被訪問的節點刪除。
調用位置:在put
方法中,如果當前插入的值不在Map
中,調用該方法。
afterNodeRemoval方法
作用:移除節點後,維護該節點前後雙向鏈表的關係。
調用位置:Map
的remove
方法中
方法二
自己實現。其中代碼是從 LeetCode 146. LRU Cache 上摘下來的。代碼裏面有註釋。
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 在鏈頭放最久未被使用的元素,鏈尾放剛剛添加或訪問的元素
*/
class LRUCache {
class Node {
int key, value;
Node pre, next;
Node(int key, int value) {
this.key = key;
this.value = value;
pre = this;
next = this;
}
}
private final int capacity;// LRU Cache的容量
private Node dummy;// dummy節點是一個冗餘節點,dummy的next是鏈表的第一個節點,dummy的pre是鏈表的最後一個節點
private Map<Integer, Node> cache;//保存key-Node對,Node是雙向鏈表節點
public LRUCache(int capacity) {
this.capacity = capacity;
dummy = new Node(0, 0);
cache = new ConcurrentHashMap<>();
}
public int get(int key) {
Node node = cache.get(key);
if (node == null) return -1;
remove(node);
add(node);
return node.value;
}
public void put(int key, int value) {
Node node = cache.get(key);
if (node == null) {
if (cache.size() >= capacity) {
cache.remove(dummy.next.key);
remove(dummy.next);
}
node = new Node(key, value);
cache.put(key, node);
add(node);
} else {
cache.remove(node.key);
remove(node);
node = new Node(key, value);
cache.put(key, node);
add(node);
}
}
/**
* 在鏈表尾部添加新節點
*
* @param node 新節點
*/
private void add(Node node) {
dummy.pre.next = node;
node.pre = dummy.pre;
node.next = dummy;
dummy.pre = node;
}
/**
* 從雙向鏈表中刪除該節點
*
* @param node 要刪除的節點
*/
private void remove(Node node) {
node.pre.next = node.next;
node.next.pre = node.pre;
}
}