現代計算機,內存還是至關昂貴的,那麼若是利用好、管理好有限的內存,來爲用戶提供更好的性能,是一個有意義的議題。html
LRU(Least Recently Used) 即最近最少使用,屬於典型的內存淘汰機制。java
通俗的說,LRU算法認爲,最近被頻繁訪問的數據會具有更高的留存,淘汰那些不常被訪問的數據。node
根據LRU算法的理念,咱們須要:
一個參數cap來做爲最大容量
一種數據結構來存儲數據,而且須要1. 輕易地更新最新的訪問的數據。2. 輕易地找出最近最少被使用的數據,當到達cap時,清理掉。
在這裏,咱們用到的數據結構是:hashmap+雙向鏈表。
1.利用hashmap的get、put方法O(1)的時間複雜度,快速取、存數據。
2.利用doublelinkedlist的特徵(能夠訪問到某個節點以前和以後的節點),實現O(1)的新增和刪除數據。git
以下圖所示:
當key2再次被使用時,它所對應的node3被更新到鏈表頭部。
假設cap=3,當key4建立、被訪問後,處於鏈表尾部的node2將被淘汰,key1將被清楚。github
節點node,存放key、val值、前節點、後節點redis
class Node{ public int key; public int val; public Node next; public Node previous; public Node() { } public Node(int key, int val) { this.key = key; this.val = val; } }
雙向鏈表,屬性有size、頭節點、尾節點。
提供api:算法
class DoubleList{ private int size; private Node head; private Node tail; public DoubleList() { this.head = new Node(); this.tail = new Node(); size = 0; head.next = tail; tail.previous = head; } public void addFirst(Node node){ Node temp = head.next; head.next = node; node.previous = head; node.next = temp; temp.previous = node; size++; } public void remove(Node node){ if(null==node|| node.previous==null|| node.next==null){ return; } node.previous.next = node.next; node.next.previous = node.previous; node.next=null; node.previous=null; size--; } public void remove(){ if(size<=0) return; Node temp = tail.previous; temp.previous.next = temp.next; tail.previous = temp.previous; temp.next = null; temp.previous=null; size--; } public int size(){ return size; } }
LRU算法實現類
APIapi
put(int key, int value)緩存
public class LRUCache { Map<Integer,Node> map; DoubleList cache; int cap; public LRUCache(int cap) { map = new HashMap<>(); cache = new DoubleList(); this.cap = cap; } public int get(int key){ Node node = map.get(key); return node==null? -1:node.val; } public void put(int key, int val){ Node node = new Node(key,val); if(map.get(key)!=null){ cache.remove(map.get(key)); cache.addFirst(node); map.put(key,node); return; } map.put(key,node); cache.addFirst(node); if(cache.size()>cap){ cache.remove(); } } public static void main(String[] args) { //test, cap = 3 LRUCache lruCache = new LRUCache(3); lruCache.put(1,1); lruCache.put(2,2); lruCache.put(3,3); //<1,1>來到鏈表頭部 lruCache.put(1,1); //<4,4>來到鏈表頭部, <2,2>被淘汰。 lruCache.put(4,4); } }
參考
LRU 策略詳解和實現
LRU原理以及應用場景