使用JavaScript實現(xiàn)LRU緩存的代碼詳解
引言
LRU(Least Recently Used)算法是一種廣泛應用于內存管理和緩存系統(tǒng)的策略,在微前端、狀態(tài)管理以及性能優(yōu)化等場景下,合理使用緩存機制能夠有效提升應用性能。本文將介紹LRU算法的基本原理,并通過JavaScript實現(xiàn)案例,幫助讀者理解其在前端開發(fā)中的應用場景。
LRU算法原理
LRU(最近最少使用)算法是一種常用的緩存淘汰策略,它假定“最近最久未使用的數(shù)據在未來被訪問的可能性最小”。當緩存空間不足時,LRU會優(yōu)先移除最近最少使用的數(shù)據,為新數(shù)據騰出存儲空間。
實現(xiàn)方式
哈希表適合快速查找、插入和刪除的場景,而雙向鏈表適合頻繁插入和刪除節(jié)點的場景。在某些情況下,這兩種數(shù)據結構也可以結合實現(xiàn)LRU緩存算法時,可以使用哈希表存儲 key 和對應的節(jié)點,雙向鏈表存儲實際的數(shù)據節(jié)點,以實現(xiàn)快速的查找和插入刪除操作。
圖解示例
假設設計一個容量為3的LRU緩存
首先添加3個元素
哈希表中依次添加數(shù)據的key值:key1,key2,key3
雙向鏈表存儲哈希對(key,value),key3是最后一個添加的(最新添加記錄是key3),那么key3對應的哈希值添加到鏈表的頭部node3。表示最近使用的。
這個時候如果要添加第四個數(shù)據,key4放入哈希表,node4放入雙向鏈表頭部,node4包含key4,value4,然而此時容量不足,需要刪除一個元素,從尾部刪除一個最久沒使用的元素,刪除上面圖示中的node1的數(shù)據,同時在哈希表中刪除node1對應的key1 把node4添加到鏈表頭部,key4添加進哈希表是同步進行的。
如果最近要訪問key3,需要把node3從當前位置刪除,并插入到鏈表頭部
簡而言之:
每次添加元素到鏈表中的時候都是從頭部添加
每次刪除元素的時候都是從尾部刪除
刪除的時候同時從哈希表里面刪除對應的key
再次訪問的元素,需要把元素移動到鏈表的頭部
實現(xiàn)代碼
使用哈希鏈表,可以在每個緩存項的節(jié)點上同時存儲鍵值信息以及指向鏈表中前后節(jié)點的引用。當一個緩存項被訪問時,先通過哈希表找到對應節(jié)點,然后將其從原有位置移出并插入鏈表尾部
class LRUCacheNode { constructor(key, value) { this.key = key; this.value = value; this.prev = null; this.next = null; } } class LRUCache { constructor(capacity = 500) { this.capacity = capacity; this.cacheMap = new Map(); // 使用哈希表存儲鍵值對 this.doubleLinkedList = new DoublyLinkedList(); // 雙向鏈表維護緩存順序 } get(key) { if (this.cacheMap.has(key)) { const node = this.cacheMap.get(key); this.doubleLinkedList.moveToTail(node); // 將節(jié)點移動到鏈表尾部,表示最新訪問 return node.value; } return -1; // 或者返回null,表示key不存在于緩存中 } put(key, value) { if (this.cacheMap.has(key)) { const node = this.cacheMap.get(key); node.value = value; this.doubleLinkedList.moveToTail(node); } else { if (this.cacheMap.size >= this.capacity) { const headNode = this.doubleLinkedList.deleteHead(); this.cacheMap.delete(headNode.key); // 移除最舊的緩存項 } const newNode = new Node(key, value); this.cacheMap.set(key, newNode); this.doubleLinkedList.addToTail(newNode); } } } // 雙向鏈表類和節(jié)點類的實現(xiàn)略(根據實際需求實現(xiàn)) class Node { constructor(key, value) { this.key = key; // 節(jié)點鍵值 this.value = value; // 節(jié)點數(shù)據值 this.prev = null; // 前驅節(jié)點引用 this.next = null; // 后繼節(jié)點引用 } } class DoublyLinkedList { constructor() { this.head = null; // 頭節(jié)點 this.tail = null; // 尾節(jié)點 } /** * 添加節(jié)點到鏈表尾部 * @param {Node} newNode 新節(jié)點 */ addToTail(newNode) { if (!this.head) { this.head = newNode; this.tail = newNode; } else { newNode.prev = this.tail; this.tail.next = newNode; this.tail = newNode; } } /** * 移除頭節(jié)點并返回 * @returns {Node | null} 刪除的頭節(jié)點或null(如果鏈表為空) */ deleteHead() { if (!this.head) return null; const deletedNode = this.head; this.head = this.head.next; if (this.head) { this.head.prev = null; } else { this.tail = null; } return deletedNode; } /** * 將指定節(jié)點移動到鏈表尾部 * @param {Node} node 需要移動的節(jié)點 */ moveToTail(node) { if (node === this.tail) return; // 如果已經是尾節(jié)點,則無需移動 // 斷開當前節(jié)點與前后節(jié)點的連接 node.prev.next = node.next; if (node.next) node.next.prev = node.prev; // 將節(jié)點添加至鏈表尾部 this.addToTail(node); } // 其他可能的方法,如查找節(jié)點、在指定位置插入節(jié)點等... }
使用場景
路由緩存
Vue.js 中的 keep-alive 組件雖然并未直接采用LRU算法,但在實際項目中,我們可以基于LRU策略自定義實現(xiàn)路由組件的緩存功能。
資源加載
對于頻繁請求且響應較慢的API,可以通過LRU緩存最近請求的結果,減少網絡請求次數(shù)。
狀態(tài)管理
在Vuex或Redux等狀態(tài)管理庫中,也可以利用LRU算法進行緩存,避免頻繁計算或獲取昂貴的狀態(tài)。
業(yè)務場景
電商大促,瀏覽器瀏覽歷史,微博熱點。實現(xiàn)的具體可能不同,但是思路均可使用LRU緩存實現(xiàn).
網頁瀏覽歷史
實現(xiàn)一個簡單的瀏覽歷史記錄功能
class LRUCache { constructor(capacity) { this.capacity = capacity; this.cache = new Map(); } get(key) { if (this.cache.has(key)) { const value = this.cache.get(key); // 刪除舊數(shù)據再重新插入,以更新最近訪問的順序 this.cache.delete(key); this.cache.set(key, value); return value; } return null; } put(key, value) { if (this.cache.has(key)) { this.cache.delete(key); } else if (this.cache.size >= this.capacity) { // 超出容量時刪除最久未訪問的數(shù)據(即最近使用頻率最低的數(shù)據) const keys = this.cache.keys(); this.cache.delete(keys.next().value); } this.cache.set(key, value); } displayHistory() { console.log("Browser History:"); for (let [key, value] of this.cache) { console.log(key + " -> " + value); } } } // 使用示例 const historyCache = new LRUCache(5); // 設置緩存容量為5 historyCache.put("Page 1", "www.page1.com"); historyCache.put("Page 2", "www.page2.com"); historyCache.put("Page 3", "www.page3.com"); historyCache.get("Page 1"); // 輸出瀏覽歷史記錄 historyCache.displayHistory();
在上面的示例中,LRUCache 類實現(xiàn)了一個簡單的 LRU 緩存,通過 get 方法獲取歷史記錄,并通過 put 方法添加歷史記錄。displayHistory 方法用于展示瀏覽歷史記錄。
可以根據實際需求進一步擴展和優(yōu)化這個示例,比如添加時間戳來記錄訪問時間、持久化歷史記錄到本地存儲等功能。希望這個示例能幫助你實現(xiàn)瀏覽歷史記錄功能。
以上就是使用JavaScript實現(xiàn)LRU緩存的代碼詳解的詳細內容,更多關于JavaScript LRU緩存的資料請關注腳本之家其它相關文章!
相關文章
.net MVC+Bootstrap下使用localResizeIMG上傳圖片
這篇文章主要為大家詳細介紹了.net MVC和Bootstrap下使用 localResizeIMG上傳圖片,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04