LRU(最近最少使用) 緩存為一段固定大小的緩存,按最近最少使用的淘汰策略對數據進行管理。
一個 LRU 緩存應當支持 put 和 get 操作:
進行 get 操作時,發生 cache miss 返回固定標識。緩存命中在返回數據的同時更新最近使用時間。
進行 put 操作時,如果 key 存在則更新數據的同時更新最近使用時間。key 不存在則新增緩存數據並將其標識位最近訪問的數據。
總的來說需要兩個數據結構復合完成對數據的存取及對訪問時間的排序。需要一張哈希表存儲鍵值對,同時可以通過一個雙向鏈表對各節點的訪問時間進行排序。
借助哈希表將存取操作的時間復雜度控制在 O(1) ,通過雙向鏈表記錄訪問順序。
public class LRUCache { /** * @Author Niuxy * @Date 2020/6/7 12:20 上午 * @Description 雙向鏈表,記錄最近訪問順序 */ private class LRUNode { LRUNode(Integer key,Integer value) { this.value = value; this.key = key; } LRUNode next; LRUNode pre; Integer value; Integer key; } //虛擬頭結點 private LRUNode firstNode; //虛擬尾結點 private LRUNode lastNode; //當前數據長度 private int size; //緩存容量 private int capacity; private Map<Integer, LRUNode> cacheMap; LRUCache(int capacity) { this.size = 0; this.capacity = capacity; this.firstNode = new LRUNode(0, Integer.MIN_VALUE); this.lastNode = new LRUNode(0, Integer.MAX_VALUE); this.cacheMap = new HashMap<Integer, LRUNode>(); firstNode.next = lastNode; lastNode.pre = firstNode; } //查找元素 public Integer get(Integer key) { LRUNode node = cacheMap.get(key); if (node == null) { return -1; } removeToHead(node); return node.value; } //新增元素 public void put(Integer key, Integer value) { LRUNode beforeNode = cacheMap.get(key); //key 已存在,覆蓋 if (beforeNode != null) { beforeNode.value = value; removeToHead(beforeNode); return; } LRUNode node = new LRUNode(key, value); cacheMap.put(key, node); putToHead(node); size++; if (size > capacity) { removeLast(); size--; } printList(); } private final void putToHead(LRUNode node) { node.next = firstNode.next; firstNode.next = node; node.pre = firstNode; node.next.pre = node; } private final void removeToHead(LRUNode node) { removeNode(node.key); putToHead(node); } private final void removeLast() { removeNode(lastNode.pre.key); } private final void removeNode(Integer key) { LRUNode node = cacheMap.get(key); if (node == null) { return; } node.pre.next = node.next; node.next.pre = node.pre; cacheMap.remove(key); } private void printList() { LRUNode node = firstNode.next; System.out.print("***"); while (node != lastNode) { System.out.print(node.key + ":" + node.value + " , "); node = node.next; } System.out.println(" --- "); } }