什么是LRU算法
LRU是Least Recently Used的縮寫,即最近最少使用,常用於頁面置換算法,為虛擬頁式存儲管理服務。LRU算法的提出,是基於這樣一個事實:在前面幾條指令中使用頻繁的頁面很可能在后面的幾條指令中頻繁使用。反過來說,已經很久沒有使用的頁面很可能在未來較長的一段時間內不會被用到。這個,就是著名的局部性原理。此外,LRU算法也經常被用作緩存淘汰策略。本文將基於LRU算法的思想,使用Java語言實現一個我們自己的緩存工具類。
算法思想:
- 新數據插入到鏈表頭部;
- 每當緩存命中(即緩存數據被訪問),則將數據移到鏈表頭部;
- 當鏈表滿的時候,將鏈表尾部的數據丟棄。
數據結構:
思考節點的結構為什么要有key val(一般不是只有val嗎)
移除尾部節點的時候需要根據節點的key來刪除映射關系(key——>node)
實現
import java.util.HashMap;
import java.util.Map;
public class LRUCache {
// 雙向鏈表節點定義
class Node {
int key;
int val;
Node prev;
Node next;
}
//模擬緩存容量
private int capacity;
//保存鏈表的頭節點和尾節點
private Node first;
private Node last;
//從key到node映射的map
private Map<Integer, Node> map;
public LRUCache(int capacity) {
this.capacity = capacity;
map = new HashMap<>(capacity);
}
public int get(int key) {
Node node = map.get(key);
//為空返回-1
if (node == null) {
return -1;
}
moveToHead(node);
return node.val;
}
public void put(int key, int value) {
//先看看是否已經存在
Node node = map.get(key);
if (node == null) {
//不存在創建節點,然后判斷緩存是否滿了,如果滿了刪除最后一個節點。然后將新節點放到鏈表頭部,增加一個映射關系
//存在則直接覆蓋,然后移動到頭部
node = new Node();
node.key = key;
node.val = value;
if(map.size() == capacity) {
removeLast();
}
addToHead(node);
map.put(key, node);
} else {
node.val = value;
moveToHead(node);
}
}
private void moveToHead(Node node) {
//要修改很多指針
if (node == first) {
return;
} else if (node == last) {
//如果是最后一個節點,將最后一個節點的next指針置為空,然后last指向前一個節點
last.prev.next = null;
last = last.prev;
} else {
//如果是中間節點,中間節點的前節點的后指針 指向 中間節點的后節點
//中間節點的后節點的前指針 指向 中間節點的前節點
node.prev.next = node.next;
node.next.prev = node.prev;
}
//把該節點作為頭結點
node.prev = first.prev;// 寫成node.prev = null;更好理解
node.next = first;
first.prev = node;
first = node;
}
private void addToHead(Node node) {
if (map.isEmpty()) {
first = node;
last = node;
} else {
//把新節點作為頭結點
node.next = first;
first.prev = node;
first = node;
}
}
private void removeLast() {
map.remove(last.key);
Node prevNode = last.prev;
//修改last所指的位置
if (prevNode != null) {
prevNode.next = null;
last = prevNode;
}
}
@Override
public String toString() {
return map.keySet().toString();
}
public static void main(String[] args) {
LRUCache cache = new LRUCache(3);
cache.put(1, 1);//【1】左邊是最近使用的
cache.put(2, 2);//【2,1】
cache.put(3, 3);//【3,2,1】
cache.get(1);//【1,3,2】
cache.put(4, 3);//【4,1,3】
System.out.println(cache);
}
}