LRU介绍和实现
LRU全稱是Least?Recently Used,即最近最久未使用的意思。
LRU算法的設計原則是:如果一個數據在最近一段時間沒有被訪問到,那么在將來它被訪問的可能性也很小。也就是說,當限定的空間已存滿數據時,應當把最久沒有被訪問到的數據淘汰。(這一段是找的,讓大家理解一下什么是LRU)。
?
說一下我們什么時候見到過LRU:其實老師們肯定都給大家舉過這么個例子:你在圖書館,你把書架子里的書拿到桌子上。。但是桌子是有限的,你有時候不得不把一些書放回去。這就相當于內存和硬盤。這個例子都說過吧?
LRU就是記錄你最長時間沒看過的書,就把它放回去。在cache那里見過吧
?
然后最近在研究redis,又看到了這個LRU,所以就想寫一下吧。
題目:設計一個結構,這個結構可以查詢K-V,但是容量有限,當存不下的時候就要把用的年代最久遠的那個東西扔掉。
其實思路很簡單,我們維護一個雙向鏈表即可,get也就是使用了,我們就把把它提到最安全的位置。新來的KV就依次放即可。
我們就先寫這個雙向鏈表結構
先寫節點結構:
public static class Node<V> {public V value;public Node<V> last;//前public Node<V> next;//后public Node(V value) {this.value = value;}}然后寫雙向鏈表結構: 我們沒必要把鏈表操作都寫了,分析一下,我們只有三個操作:
1、加節點
2、使用了某個節點就把它調到尾,代表優先級最高
3、把優先級最低的移除,也就是去頭部
(不會的,翻我之前的鏈表操作都有寫)
public static class NodeDoubleLinkedList<V> {private Node<V> head;//頭private Node<V> tail;//尾public NodeDoubleLinkedList() {this.head = null;this.tail = null;}public void addNode(Node<V> newNode) {if (newNode == null) {return;}if (this.head == null) {//頭空this.head = newNode;this.tail = newNode;} else {//頭不空this.tail.next = newNode;newNode.last = this.tail;//注意讓本節點前指針指向舊尾this.tail = newNode;//指向新尾}} /*某個點移到最后*/public void moveNodeToTail(Node<V> node) {if (this.tail == node) {//是尾return;}if (this.head == node) {//是頭this.head = node.next;this.head.last = null;} else {//中間node.last.next = node.next;node.next.last = node.last;}node.last = this.tail;node.next = null;this.tail.next = node;this.tail = node;} /*刪除第一個*/public Node<V> removeHead() {if (this.head == null) {return null;}Node<V> res = this.head;if (this.head == this.tail) {//就一個this.head = null;this.tail = null;} else {this.head = res.next;res.next = null;this.head.last = null;}return res;}}鏈表操作封裝完了就要實現這個結構了。
具體思路代碼注釋
public static class MyCache<K, V> {//為了kv or vk都能查private HashMap<K, Node<V>> keyNodeMap;private HashMap<Node<V>, K> nodeKeyMap;//用來做優先級private NodeDoubleLinkedList<V> nodeList;private int capacity;//容量public MyCache(int capacity) {if (capacity < 1) {//你容量連1都不給,搗亂呢throw new RuntimeException("should be more than 0.");}this.keyNodeMap = new HashMap<K, Node<V>>();this.nodeKeyMap = new HashMap<Node<V>, K>();this.nodeList = new NodeDoubleLinkedList<V>();this.capacity = capacity;}public V get(K key) {if (this.keyNodeMap.containsKey(key)) {Node<V> res = this.keyNodeMap.get(key);this.nodeList.moveNodeToTail(res);//使用過了就放到尾部return res.value;}return null;}public void set(K key, V value) {if (this.keyNodeMap.containsKey(key)) {Node<V> node = this.keyNodeMap.get(key);node.value = value;//放新vthis.nodeList.moveNodeToTail(node);//我們認為放入舊key也是使用過} else {Node<V> newNode = new Node<V>(value);this.keyNodeMap.put(key, newNode);this.nodeKeyMap.put(newNode, key);this.nodeList.addNode(newNode);//加進去if (this.keyNodeMap.size() == this.capacity + 1) {this.removeMostUnusedCache();//放不下就去掉優先級最低的}}}private void removeMostUnusedCache() {//刪除頭Node<V> removeNode = this.nodeList.removeHead();K removeKey = this.nodeKeyMap.get(removeNode);//刪除掉兩個map中的記錄this.nodeKeyMap.remove(removeNode);this.keyNodeMap.remove(removeKey);}}?
總結
- 上一篇: 李牛(Linux)脚本
- 下一篇: UNIX(进程间通信):05---守护进