LRU算法

引至【想不到!面試官問我:Redis 內存滿了怎麼辦?】,本文只關注其中的LRU算法java

LRU(Least Recently Used),即最近最少使用,是一種緩存置換算法。在使用內存做爲緩存的時候,緩存的大小通常是固定的。當緩存被佔滿,這個時候繼續往緩存裏面添加數據,就須要淘汰一部分老的數據,釋放內存空間用來存儲新的數據。這個時候就能夠使用LRU算法。其核心思想是:若是一個數據在最近一段時間沒有被用到,那麼未來被使用到的可能性也很小,因此就能夠被淘汰掉。node

使用java實現一個簡單的LRU算法

主要思想:LRU內部實現一個Node<k,v>類,該類包含key、value,前一節點pre和後一節點next四個屬性;LRU中包含一個Map<k, Node<k, v>> nodeMap的緩存map,包含全部的數據,哨兵 Node<k, v> head和 Node<k, v> tail標註鏈表的頭部和尾部。當存入數據時(先判斷LRU是否已超長,若是超長則須要刪除數據)判斷nodeMap是否包含該節點,若是不包含,則將該節點添加至nodeMap中,並將該節點插入至head後(即該節點pre設置爲head,head節點的next設置爲當前節點,該節點的next設置爲head節點的的next);若是包含該數據,則須要將該node從原鏈表中刪除,而後再將該數據插入至head後便可(將該節點的pre設置爲head,head節點的next設置爲當前節點)。當數據被get時,也須要先將該node從原鏈表中刪除,而後再將該數據插入至head後(將該節點的pre設置爲head,head節點的next設置爲當前節點),即實現了LRU.面試

  1 public class LRUCache<k, v> {
  2     //容量
  3     private int capacity;
  4     //當前有多少節點的統計
  5     private int count;
  6     //緩存節點
  7     private Map<k, Node<k, v>> nodeMap;
  8     private Node<k, v> head;
  9     private Node<k, v> tail;
 10 
 11     public LRUCache(int capacity) {
 12         if (capacity < 1) {
 13             throw new IllegalArgumentException(String.valueOf(capacity));
 14         }
 15         this.capacity = capacity;
 16         this.nodeMap = new HashMap<>();
 17         //初始化頭節點和尾節點,利用哨兵模式減小判斷頭結點和尾節點爲空的代碼
 18         Node headNode = new Node(null, null);
 19         Node tailNode = new Node(null, null);
 20         headNode.next = tailNode;
 21         tailNode.pre = headNode;
 22         this.head = headNode;
 23         this.tail = tailNode;
 24     }
 25 
 26     public void put(k key, v value) {
 27         Node<k, v> node = nodeMap.get(key);
 28         if (node == null) {
 29             if (count >= capacity) {
 30                 //先移除一個節點
 31                 removeNode();
 32             }
 33             node = new Node<>(key, value);
 34             //添加節點
 35             addNode(node);
 36         } else {
 37             //移動節點到頭節點
 38             moveNodeToHead(node);
 39         }
 40     }
 41 
 42     public Node<k, v> get(k key) {
 43         Node<k, v> node = nodeMap.get(key);
 44         if (node != null) {
 45             moveNodeToHead(node);
 46         }
 47         return node;
 48     }
 49 
 50     private void removeNode() {
 51         Node node = tail.pre;
 52         //從鏈表裏面移除
 53         removeFromList(node);
 54         nodeMap.remove(node.key);
 55         count--;
 56     }
 57 
 58     private void removeFromList(Node<k, v> node) {
 59         Node pre = node.pre;
 60         Node next = node.next;
 61 
 62         pre.next = next;
 63         next.pre = pre;
 64 
 65         node.next = null;
 66         node.pre = null;
 67     }
 68 
 69     private void addNode(Node<k, v> node) {
 70         //添加節點到頭部
 71         addToHead(node);
 72         nodeMap.put(node.key, node);
 73         count++;
 74     }
 75 
 76     private void addToHead(Node<k, v> node) {
 77         Node next = head.next;
 78         next.pre = node;
 79         node.next = next;
 80         node.pre = head;
 81         head.next = node;
 82     }
 83 
 84     public void moveNodeToHead(Node<k, v> node) {
 85         //從鏈表裏面移除
 86         removeFromList(node);
 87         //添加節點到頭部
 88         addToHead(node);
 89     }
 90 
 91     class Node<k, v> {
 92         k key;
 93         v value;
 94         Node pre;
 95         Node next;
 96 
 97         public Node(k key, v value) {
 98             this.key = key;
 99             this.value = value;
100         }
101     }
102 }
相關文章
相關標籤/搜索