自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

手寫最簡單的LRU算法

開發(fā) 前端 算法
LRU(Least recently used)最近最少使用,它的核心思想是“如果數(shù)據(jù)最近被訪問過,那么將來被訪問的幾率也更高”。因此 LRU 算法會根據(jù)數(shù)據(jù)的歷史訪問記錄來進行排序,如果空間不足,就會淘汰掉最近最少使用的數(shù)據(jù)。

1 什么是LRU

LRU(Least recently used)最近最少使用,它的核心思想是“如果數(shù)據(jù)最近被訪問過,那么將來被訪問的幾率也更高”。因此 LRU 算法會根據(jù)數(shù)據(jù)的歷史訪問記錄來進行排序,如果空間不足,就會淘汰掉最近最少使用的數(shù)據(jù)。

2 LRU 實現(xiàn)原理

由于 LRU 算法會將最近使用的數(shù)據(jù)優(yōu)先級上升,因此需要數(shù)據(jù)結(jié)構(gòu)支持排序,鏈表非常合適。

為什么不考慮數(shù)組呢?

由于 LRU 算法,一般都會應(yīng)用在訪問比較頻繁的場景,因此,對數(shù)據(jù)的移動會頻繁,而數(shù)組一旦移動,需要將移動到值的位置后面的所有數(shù)據(jù)的位置全部改變,效率比較低,不推薦使用。

3 雙向鏈表之LinkedHashMap

前面我們分析到 LRU 的算法實現(xiàn),可以使用鏈表實現(xiàn),java 中 LinkedHashMap 就是一個雙向鏈表。

LinkedHashMap是HashMap的子類,在HashMap數(shù)據(jù)結(jié)構(gòu)的基礎(chǔ)上,還維護著一個雙向鏈表鏈接所有entry,這個鏈表定義了迭代順序,通常是數(shù)據(jù)插入的順序。

我們來看看LinkedHashMap的源碼:

從源碼中的定義可以看到,accessOrder 屬性可以指定遍歷 LinkedHashMap 的順序,true 表示按照訪問順序,false 表示按照插入順序,默認(rèn)為 false。

由于LRU對訪問順序敏感,因此使用true來簡單驗證一下:

  1. public class LRUTest { 
  2.     public static void main(String[] args) { 
  3.         LinkedHashMap<String, Object> map = new LinkedHashMap<>(16, 0.75f, true); 
  4.         map.put("a", 1); 
  5.         map.put("b", 2); 
  6.         map.put("c", 3); 
  7.         System.out.println("before get " + map); 
  8.         map.get("a"); 
  9.         System.out.println("after get" + map); 
  10.     }} 

運行結(jié)果如下:

  1. before get {a=1, b=2, c=3} 
  2. after get{b=2, c=3, a=1} 

可以看到通過 accessOrder = true,可以讓 LinkedHashMap 按照訪問順序進行排序。

那么 LinkedHashMap 是怎么做的呢?

我們看下get方法

  1. public V get(Object key) { 
  2.     Node<K,V> e; 
  3.     // 獲取node 
  4.     if ((e = getNode(hash(key), key)) == null
  5.         return null
  6.     // 如果 accessOrder = true,則執(zhí)行afterNodeAccess方法 
  7.     if (accessOrder) 
  8.         afterNodeAccess(e); 
  9.     return e.value; 

再看下afterNodeAccess方法,發(fā)現(xiàn)進行移動節(jié)點,到此移動節(jié)點的原理我們了解了

  1. void afterNodeAccess(Node<K,V> e) { // move node to last 
  2.    LinkedHashMap.Entry<K,V> last
  3.    if (accessOrder && (last = tail) != e) { 
  4.        LinkedHashMap.Entry<K,V> p =            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;        p.after = null;        if (b == null
  5.            head = a;        else 
  6.            b.after = a;        if (a != null
  7.            a.before = b;        else 
  8.            last = b; 
  9.        if (last == null
  10.            head = p;        else { 
  11.            p.before = last
  12.            last.after = p;        }        tail = p;        ++modCount;    }} 

目前,如果使用 LinkedHashMap 做LRU,還有一個問題困擾著我們,就是如果容量有限,該如何淘汰舊數(shù)據(jù)?

我們回過頭看看 put 方法

  1. public V put(K key, V value) { 
  2.     return putVal(hash(key), key, value, falsetrue); 
  3. final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) { 
  4.     Node<K,V>[] tab; Node<K,V> p; int n, i; 
  5.     if ((tab = table) == null || (n = tab.length) == 0) 
  6.         n = (tab = resize()).length; 
  7.     if ((p = tab[i = (n - 1) & hash]) == null
  8.         tab[i] = newNode(hash, key, value, null); 
  9.     else { 
  10.         Node<K,V> e; K k; 
  11.         if (p.hash == hash && 
  12.             ((k = p.key) == key || (key != null && key.equals(k)))) 
  13.             e = p; 
  14.         else if (p instanceof TreeNode) 
  15.             e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); 
  16.         else { 
  17.             for (int binCount = 0; ; ++binCount) { 
  18.                 if ((e = p.next) == null) { 
  19.                     p.next = newNode(hash, key, value, null); 
  20.                     if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 
  21.                         treeifyBin(tab, hash); 
  22.                     break; 
  23.                 } 
  24.                 if (e.hash == hash && 
  25.                     ((k = e.key) == key || (key != null && key.equals(k)))) 
  26.                     break; 
  27.                 p = e; 
  28.             } 
  29.         } 
  30.         if (e != null) { // existing mapping for key 
  31.             V oldValue = e.value; 
  32.             if (!onlyIfAbsent || oldValue == null
  33.                 e.value = value; 
  34.             afterNodeAccess(e); 
  35.             return oldValue; 
  36.         } 
  37.     } 
  38.     ++modCount; 
  39.     if (++size > threshold) 
  40.         resize(); 
  41.     afterNodeInsertion(evict); 
  42.     return null
  43. void afterNodeInsertion(boolean evict) { // possibly remove eldest 
  44.     LinkedHashMap.Entry<K,V> first
  45.     if (evict && (first = head) != null && removeEldestEntry(first)) { 
  46.         K key = first.key
  47.         removeNode(hash(key), keynullfalsetrue); 
  48.     } 

從put方法中逐步看下來,最終我們發(fā)現(xiàn),如果 removeEldestEntry(first) 方法返回true,則會移除 head,這樣就淘汰了最近都沒使用的數(shù)據(jù)。完全符合LRU。

4 最簡單的LRU實現(xiàn)

根據(jù)上面分析,我們可以如下實現(xiàn)一個最簡單的LRU

  1. public class LRUCache<K,V> extends LinkedHashMap<K,V> { 
  2.       private int cacheSize; 
  3.     public LRUCache(int cacheSize) { 
  4.       // 注意:此處需要讓 accessOrder = true 
  5.       super(cacheSize, 0.75f, true); 
  6.       this.cacheSize = cacheSize; 
  7.   } 
  8.   /** 
  9.    * 判斷元素個數(shù)是否超過緩存的容量,超過需要移除 
  10.    */ 
  11.   @Override 
  12.   protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { 
  13.       return size() > cacheSize; 
  14.   } 

 

責(zé)任編輯:未麗燕 來源: 今日頭條
相關(guān)推薦

2021-03-01 18:42:02

緩存LRU算法

2021-07-15 14:29:06

LRU算法

2020-05-15 17:05:51

Oracle數(shù)據(jù)庫LRU算法

2019-12-24 10:32:01

OracleLRU臟塊

2020-10-30 11:30:15

Least Recen

2022-06-17 07:49:14

緩存LRU

2022-05-09 19:59:15

RedisLRU 算法

2021-09-05 18:29:58

Linux內(nèi)存回收

2023-07-06 12:39:14

RedisLRULFU

2020-02-19 19:18:02

緩存查詢速度淘汰算法

2009-07-23 11:11:18

LRU緩存

2015-07-29 10:31:16

Java緩存算法

2012-10-31 09:16:36

IT管理

2017-04-20 09:21:44

pythonLRU算法

2022-10-31 08:27:53

Database數(shù)據(jù)數(shù)據(jù)庫

2013-11-06 09:56:58

2022-03-14 08:01:06

LRU算法線程池

2011-03-08 10:28:40

Proftpd

2017-08-09 15:27:33

python爬蟲開發(fā)工具

2011-03-29 14:35:34

點贊
收藏

51CTO技術(shù)棧公眾號