It's always the attention to detail and the little grace notes that really make something sing. 專注細節愈出彩,別緻心思定成敗。
本文主要分享了LRU 快取淘汰演算法兩種實現。重要的不是實現,而且思想!
所有原始碼均已上傳至github:連結
定義
LRU(Least Recently Used)最近最少使用策略就像它的名字一樣,是根據資料的歷史訪問記錄來進行淘汰資料的,其思想是“如果資料最近被訪問過,那麼將來被訪問的機率也更高;長期不被使用的資料在將來用到的機率也不大;當資料所佔記憶體達到一定的閾值時,將移除最近最少被使用的資料”。
舉例
比如一個書櫃(容量為10),我會把我自己的書籍放進去,其中有兩本書籍是我最喜歡的,經常翻閱,但是隨著我的購買,書櫃會逐漸放滿(記憶體溢位),這時候就需要用到LRU的思想了,把我不經常看的,從書櫃裡拿出,放進箱子裡,然後再把新買的書籍放進書櫃。
再 比如Redis,它是基於記憶體的,但是記憶體也不是無窮大的,當記憶體佔用達到一個閾值的時候,它就可以使用LRU等一系列快取演算法,或者是將資料存到硬碟裡。
具體的實現程式碼如下:
基於連結串列
連結串列初始化,申請capacity大小的記憶體空間
private LRUByLinkedList(int capacity) {
head = null;
size = capacity;
count = 0;
}複製程式碼
模擬LRU的訪問(如果沒有該資料,則插入頭部)
注意:該if-else語句不能交換順序,否則會出現連結串列已滿,並且該資料已存在的情況無法處理。
private void insert(int data) {
Node preNode = findNode(data);
if (null != preNode) {
delete(preNode);
} else {
if (count >= size) {//連結串列滿
deleteToTail();
}
}
insertToHead(data);
}複製程式碼
刪除指定資料方法,常規刪除
private void delete(Node preNode) {
System.out.println("刪除指定元素:" + preNode.next.data);
preNode.next = preNode.next.next;
--count;
}複製程式碼
刪除連結串列尾部資料
private void deleteToTail() {
Node node = head;
Node curNode = null;
while (null != node.next) {
curNode = node;
node = node.next;
}
if (null != curNode) {
System.out.println("連結串列已滿,刪除尾部元素:" + curNode.next.data);
curNode.next = null;
}
--count;
}複製程式碼
在頭部插入資料
private void insertToHead(int data) {
Node node = new Node(data, null);
if (null == head) {
head = node;
} else {
node.next = head;
}
head = node;
++count;
}
複製程式碼
測試結果如下
- 首先將capacity大小的連結串列插滿
- 當插入capacity+1個資料時,需要刪除尾部資料
- 當插入的資料存在的時候,將它從其位置刪除,並且插入頭部
基於陣列
資料比較連結串列實現起來更為簡單,在此不做闡述,直接上程式碼。
private LRUByArray(int capacity) {
arrays = new int[capacity];
size = capacity;
count = 0;
}複製程式碼
private void insert(int data) {
int index = findValue(data);
if (-1 != index) {
delete(index);
} else {
if (count >= size) {//陣列滿
deleteToTail();
}
}
insertToHead(data);
}複製程式碼
private void delete(int index) {
//通過資料遷移的方式將該值刪除
for (int i = index + 1; i < count; i++) {
arrays[i - 1] = arrays[i];
}
System.out.println("刪除元素...");
--count;
}複製程式碼
private void deleteToTail() {
--count;//標記刪除法,實際上陣列還存有該元素
System.out.println("刪除尾部元素...");
}複製程式碼
private void insertToHead(int data) {
if (count > 1) {
for (int i = count - 1; i > -1; --i) {
arrays[i + 1] = arrays[i];
}
}
arrays[0] = data;
++count;
}複製程式碼
測試結果如下
注意:因為有大量頻繁的訪問導致資料遷移頻繁,因此陣列並不適合做這種事情,可以考慮加一個HaspMap做快取,避免資料的頻繁遷移。
end
您的點贊和關注是對我最大的支援,謝謝!