模仿LinkedHashMap來進行LRU演算法
阿新 • • 發佈:2018-12-04
讓我們以使用者資訊的需求為例,來演示一下LRU演算法的基本思路:
1.假設我們使用雜湊連結串列來快取使用者資訊,目前快取了4個使用者,這4個使用者是按照時間順序依次從連結串列右端插入的。
2.此時,業務方訪問使用者5,由於雜湊連結串列中沒有使用者5的資料,我們從資料庫中讀取出來,插入到快取當中。這時候,連結串列中最右端是最新訪問到的使用者5,最左端是最近最少訪問的使用者1。
3.接下來,業務方訪問使用者2,雜湊連結串列中存在使用者2的資料,我們怎麼做呢?我們把使用者2從它的前驅節點和後繼節點之間移除,重新插入到連結串列最右端。這時候,連結串列中最右端變成了最新訪問到的使用者2,最左端仍然是最近最少訪問的使用者1。
4.接下來,業務方請求修改使用者4的資訊。同樣道理,我們把使用者4從原來的位置移動到連結串列最右側,並把使用者資訊的值更新。這時候,連結串列中最右端是最新訪問到的使用者4,最左端仍然是最近最少訪問的使用者1。
5.後來業務方換口味了,訪問使用者6,使用者6在快取裡沒有,需要插入到雜湊連結串列。假設這時候快取容量已經達到上限,必須先刪除最近最少訪問的資料,那麼位於雜湊連結串列最左端的使用者1就會被刪除掉,然後再把使用者6插入到最右端。
以上,就是LRU演算法的基本思路。
import java.util.HashMap; public class TestLRU { //頭 private Node head; //尾 private Node end; // 快取儲存上限 private int limit; private HashMap<String, Node> hashMap; public TestLRU(int limit) { this.limit = limit; hashMap = new HashMap<String, Node>(); } public String get(String key) { Node node = hashMap.get(key); if (node == null) { return null; } refreshNode(node); return node.value; } public void put(String key, String value) { Node node = hashMap.get(key); if (node == null) { // 如果key不存在,插入key-value if (hashMap.size() >= limit) { String oldKey = removeNode(head); hashMap.remove(oldKey); } node = new Node(key, value); addNode(node); hashMap.put(key, node); } else { // 如果key存在,重新整理key-value node.value = value; refreshNode(node); } } public void remove(String key) { Node node = hashMap.get(key); removeNode(node); hashMap.remove(key); } /** * 重新整理被訪問的節點位置 * * @param node 被訪問的節點 */ private void refreshNode(Node node) { // 如果訪問的是尾節點,無需移動節點 if (node == end) { return; } // 移除節點 removeNode(node); // 重新插入節點 addNode(node); } /** * 刪除節點 * * @param node 要刪除的節點 */ private String removeNode(Node node) { if (node == end) { // 移除尾節點 end = end.pre; } else if (node == head) { // 移除頭節點 head = head.next; } else { // 移除中間節點 node.pre.next = node.next; node.next.pre = node.pre; } return node.key; } /** * 尾部插入節點 * * @param node 要插入的節點 */ private void addNode(Node node) { if (end != null) { end.next = node; node.pre = end; node.next = null; } end = node; //第一個節點 if (head == null) { head = node; } } class Node { Node(String key, String value) { this.key = key; this.value = value; } public Node pre; public Node next; public String key; public String value; } public static void main(String[] args) { TestLRU lruCache = new TestLRU(5); lruCache.put("001", "使用者1資訊"); lruCache.put("002", "使用者2資訊"); lruCache.put("003", "使用者3資訊"); lruCache.put("004", "使用者4資訊"); lruCache.put("005", "使用者5資訊"); lruCache.get("001"); lruCache.put("004", "使用者4資訊更新"); lruCache.put("006", "使用者6資訊"); System.out.println(lruCache.get("001")); System.out.println(lruCache.get("002")); System.out.println(lruCache.get("006")); } }