Java實現LRU算法


一。LRU算法簡介

LRU(Least Recently Used)最近最久未使用算法

常見應用場景:內存管理中的頁面置換算法、緩存淘汰中的淘汰策略等

 

二。實現理論

  底層結構:雙向鏈表 + HashMap ,雙向鏈表由特定的哈希節點組成。

(1)訪問節點時,將其從原來位置刪除,插入到雙向鏈表頭部;
(2)更新節點時,先刪除原有緩存數據(即原有節點),然后更新map映射,再將更新值作為節點插入鏈表頭;更新后,判斷容量是否超過最大內存使用量
(3)超過則執行淘汰;淘汰即刪除雙向鏈表最后一個節點,同時刪除map中的映射
(4)LRU實現中有頻繁的查找節點並刪除,為節省時間(鏈表查找目標節點需要遍歷),使用HashMap保存鍵-節點映射關系,O(1)的查找+O(1)的刪除
(5)LRU實現中,要頻繁的在頭部插入,以及在尾部刪除;因此,需要定義head、tail兩個節點,方便操作
 
三。代碼
  1 package com.xl.Base;
  2 
  3 import java.util.HashMap;
  4 import java.util.Iterator;
  5 
  6 /**
  7  *    最近最久未使用淘汰策略
  8  *    基於 雙向鏈表 + 哈希表組成,其中雙向鏈表由哈希鏈表節點構成
  9  *    封裝為 LRU(K, V)
 10  *    對外提供 get(K)訪問數據、put(K, V)更新數據、Iterator()遍歷數據
 11  */
 12 public class LRU<K, V> implements Iterable<K>{
 13     
 14     private Node head;
 15     private Node tail;
 16     //記錄K-Node映射,便於快速查找目標數據對應節點
 17     private HashMap<K, Node> map;
 18     private int maxSize;
 19     
 20     //哈希鏈表節點類 Node
 21     private class Node{
 22         Node pre;
 23         Node next;
 24         K k;
 25         V v;
 26         
 27         //Node對外提供構造方法
 28         public Node(K k, V v) {
 29             this.k = k;
 30             this.v = v;
 31         }
 32     }
 33     
 34     //初始化時必須傳入最大可用內存容量
 35     public LRU(int maxSize){
 36         this.maxSize = maxSize;
 37         //HashMap初始容量設置為 maxSize * 4/3,即達到最大可用內存時,HashMap也不會自動擴容浪費空間
 38         this.map = new HashMap<>(maxSize * 4 / 3);
 39         
 40         head.next = tail;
 41         tail.pre = head;
 42     }
 43 
 44     //獲取指定數據
 45     private V get(K key) {
 46         //判斷是否存在對應數據
 47         if(!map.containsKey(key)) {
 48             return null;
 49         }
 50         
 51         //最新訪問的數據移動到鏈表頭
 52         Node node = map.get(key);
 53         remove(node);
 54         addFirst(node);
 55         return node.v;
 56     }
 57     
 58     //更新舊數據或添加數據
 59     private void put(K key, V value) {
 60         //若存在舊數據則刪除
 61         if(map.containsKey(key)) {
 62             Node node = map.get(key);
 63             remove(node);
 64         }
 65         
 66         //新數據對應節點插入鏈表頭
 67         Node node = new Node(key, value);
 68         map.put(key, node);
 69         addFirst(node);
 70         
 71         //判斷是否需要淘汰數據
 72         if(map.size() > maxSize) {
 73             removeLast();
 74             //數據節點淘汰后,同時刪除map中的映射
 75             map.remove(node.k);
 76         }
 77     }
 78     
 79     //將指定節點插入鏈表頭
 80     private void addFirst(Node node) {
 81         Node next = head.next;
 82         
 83         head.next = node;
 84         node.pre = head;
 85         
 86         node.next = next;
 87         next.pre = node;
 88     }
 89     
 90     //從鏈表中刪除指定節點
 91     private void remove(Node node) {
 92         Node pre = node.pre;
 93         Node next = node.next;
 94         
 95         pre.next = next;
 96         next.pre = pre;
 97         
 98         node.next = null;
 99         node.pre = null;
100     }
101     
102     //淘汰數據
103     private Node removeLast() {
104         //找到最近最久未使用的數據所對應節點
105         Node node = tail.pre;
106         
107         //淘汰該節點
108         remove(node);
109         
110         return node;
111     }
112     
113     //通過迭代器遍歷所有數據對應鍵
114     @Override
115     public Iterator<K> iterator() {
116         return new Iterator<K>() {
117             
118             private Node cur = head.next;
119 
120             @Override
121             public boolean hasNext() {
122                 return cur != tail;
123             }
124 
125             @Override
126             public K next() {
127                 Node node = cur;
128                 cur = cur.next;
129                 return node.k;
130             }
131             
132         };
133     }
134     
135 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM