LRU 是 Least Recently Used 的縮寫,即最近最少使用,是一種常用的頁面置換算法,選擇內存中最近最久未使用的頁面予以淘汰。
可用的 NodeJS 庫見node-lru-cache
然怎么使用 JS 簡單寫一個?類似的題目見 LeetCode 146 LRU 緩存機制,進階要求時間復雜度 O(1) 。
思路
解法:維護一個數組,提供 get 和 put 方法,並且限定 max 數量。
使用時,get 可以標記某個元素是最新使用的,提升它去第一項。put 可以加入某個key-value,但需要判斷是否已經到最大限制 max
- 若未到能直接往數組第一項里插入
- 若到了最大限制 max,則需要淘汰數據尾端一個元素。
LRUCache cache = new LRUCache( 2 /* 緩存容量 */ );
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 該操作會使得密鑰 2 作廢
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 該操作會使得密鑰 1 作廢
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
具體代碼
一般的解法,通過維護一個數組,數組項存放了 key-value 鍵值對對象,每次需要遍歷去尋找 key 值所在的數組下標操作。
已經通過 leetCode 146 的檢測。執行用時 : 720 ms。內存消耗 : 58.5 MB。
var LRUCache = function (capacity) {
this.capacity = capacity;
this.cache = [];
};
/**
* @param {number} key
* @return {number}
*/
LRUCache.prototype.get = function (key) {
let index = this.cache.findIndex((item) => item.key === key);
if (index === -1) {
return -1;
}
// 刪除此元素后插入到數組第一項
let value = this.cache[index].value;
this.cache.splice(index, 1);
this.cache.unshift({
key,
value,
});
return value;
};
/**
* @param {number} key
* @param {number} value
* @return {void}
*/
LRUCache.prototype.put = function (key, value) {
let index = this.cache.findIndex((item) => item.key === key);
// 想要插入的數據已經存在了,那么直接提升它就可以
if (index > -1) {
this.cache.splice(index, 1);
} else if (this.cache.length >= this.capacity) {
// 若已經到達最大限制,先淘汰一個最久沒有使用的
this.cache.pop();
}
this.cache.unshift({ key, value });
};
上面的做法其實有變種,可以通過一個對象來存鍵值對,一個數組來存放鍵的順序。
來看進階要求
時間復雜度 O(1),那就不能數組遍歷去查找 key 值。可以用 ES6 的 Map 來解了,因為 Map 既能保持鍵值對,還能記住插入順序。
var LRUCache = function (capacity) {
this.cache = new Map();
this.capacity = capacity;
};
LRUCache.prototype.get = function (key) {
if (this.cache.has(key)) {
// 存在即更新
let temp = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, temp);
return temp;
}
return -1;
};
LRUCache.prototype.put = function (key, value) {
if (this.cache.has(key)) {
// 存在即更新(刪除后加入)
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// 不存在即加入
// 緩存超過最大值,則移除最近沒有使用的
this.cache.delete(this.cache.keys().next().value);
}
this.cache.set(key, value);
};
上述代碼來自LRU 緩存機制-官方,執行用時 : 228 ms,內存消耗 : 59 MB
使用案例
- vue 2.6 keep-alive的實現原理和緩存策略法
- 多點登錄,限制一個賬號允許登錄 5 個端,那么第 6 個端登錄時,就需要擠掉最早登錄的那個端。