LRU算法原理及其實現

LRU是什麼

現代計算機,內存還是至關昂貴的,那麼若是利用好、管理好有限的內存,來爲用戶提供更好的性能,是一個有意義的議題。html

LRU(Least Recently Used) 即最近最少使用,屬於典型的內存淘汰機制。java

通俗的說,LRU算法認爲,最近被頻繁訪問的數據會具有更高的留存,淘汰那些不常被訪問的數據。node

LRU算法實現思路

根據LRU算法的理念,咱們須要:
一個參數cap來做爲最大容量
一種數據結構來存儲數據,而且須要1. 輕易地更新最新的訪問的數據。2. 輕易地找出最近最少被使用的數據,當到達cap時,清理掉。
在這裏,咱們用到的數據結構是:hashmap+雙向鏈表。
1.利用hashmap的get、put方法O(1)的時間複雜度,快速取、存數據。
2.利用doublelinkedlist的特徵(能夠訪問到某個節點以前和以後的節點),實現O(1)的新增和刪除數據。git

以下圖所示:
image
當key2再次被使用時,它所對應的node3被更新到鏈表頭部。
image
假設cap=3,當key4建立、被訪問後,處於鏈表尾部的node2將被淘汰,key1將被清楚。github

LRU的簡單實現

節點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:算法

  • addFirst(): 頭插法入鏈表
  • remove(): 刪除最後一個節點
  • remove(Node node):刪除特定節點
  • size():獲取鏈表長度
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

  • get(int key): 爲null返回-1
  • put(int key, int value)緩存

    • 若map中有,刪除原節點,增長新節點
    • 若map中沒有,map和鏈表中新增新數據。
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應用場景

  • 底層的內存管理,頁面置換算法
  • 通常的緩存服務,memcache\redis之類
  • 部分業務場景
參考
LRU 策略詳解和實現
LRU原理以及應用場景
相關文章
相關標籤/搜索