題目描述
題目思路
-
因爲需要o(1)級別的時間完成緩存操作,決定了我們對數據結構的設計需要快速的查找,刪除(刪除長時間未使用的),因爲單列表具有不錯的插入,刪除性能,但是任然不能達到O(1)級別,因此採用雙向列表進行記錄緩存元素的使用情況,雙向列表在插入和刪除上能做到O(1)級別的操作方式。
-
因爲需要優秀的讀取性能,因此考慮使用HashMap進行存取。
思路設計
- 因爲設計到雙向列表,因此數據存儲的數據結構設計爲Node節點。
- 對於雙向列表需要提供功能爲:
- 需要提供節點刪除功能。
- 需要提供節點切換功能,即需要把讀取的節點提升到最活躍的情況。
- 需要提供刪除最久未使用的節點功能。
- 對於緩存實現幫助類,我們需要實現如下方法:
- 獲取對象的時候,需要把已經存在的對象活躍度刷新到最活躍的狀態。
- 放入對象的時候,需要把對象同步到hashmap和狀態列表中去。
代碼實現
import java.util.HashMap;
/**
* @description:
* @author: lilang
* @version:
* @modified By:[email protected]
*/
public class LRUCache {
private class Node {
private Node next;
private Node pre;
private int key;
private int val;
public Node(int key, int val) {
this.key = key;
this.val = val;
}
}
private class TwoWayList {
private Node first;
private Node last;
private int size;
/**
* 初始化雙向列表,新加入的節點會放到雙向列表頭尾節點之間
*
* 活躍度判斷:新加入的節點都統一放到末尾,因此越靠近末尾的節點越活越
*/
public TwoWayList() {
first = new Node(0, 0);
last = new Node(0, 0);
first.next = last;
last.pre = first;
this.size = 0;
}
/**
* 節點添加到末尾,新加入的節點和上一個節點和尾部節點建立雙向連接
* 同時列表長度加一
* @param node
*/
public void addNodeToLast(Node node) {
node.pre = last.pre;
node.next = last;
last.pre.next = node;
last.pre = node;
size++;
}
/**
* 刪除某個節點,進入來需要刪除的節點,必須存在
*
* @param node
*/
public void remove(Node node) {
node.pre.next = node.next;
node.next.pre = node.pre;
size--;
}
/**
* 返回刪除後的節點
*
* @return
*/
public Node removeFirst() {
/**
* 默認雙向列表保持兩個頭尾節點
*/
if (first.next.next == null) {
return null;
}
Node removeNode = first.next;
remove(first.next);
return removeNode;
}
public int getSize() {
return size;
}
}
private int capacity;
/**
* hashmap 存儲node有一個好處就是可以獲得node的地址信息
*
*/
private HashMap<Integer, Node> cacheMap;
private TwoWayList twoWayList;
public LRUCache(int capacity) {
this.capacity = capacity;
this.cacheMap = new HashMap();
this.twoWayList = new TwoWayList();
}
public int get(int key) {
Node node = cacheMap.get(key);
if (node == null) {
return -1;
}
makeNodeRecently(node);
return node.val;
}
public void put(int key, int value) {
Node node = cacheMap.get(key);
if (node == null){
if (twoWayList.getSize() == this.capacity) {
removeLeastRecently();
}
addRecently(key, value);
}else {
node.val = value;
cacheMap.put(key,node);
/**
* 提升當前節點的活躍度
*/
makeNodeRecently(node);
}
}
/* 將某個Node提升爲最近使用的 */
private void makeNodeRecently(Node node) {
twoWayList.remove(node);
twoWayList.addNodeToLast(node);
}
/* 添加最近使用的元素 */
private void addRecently(int key, int val) {
Node node = new Node(key, val);
twoWayList.addNodeToLast(node);
cacheMap.put(key, node);
}
/* 刪除最久未使用的元素 */
private void removeLeastRecently() {
Node node = twoWayList.removeFirst();
if (node != null) {
cacheMap.remove(node.key);
}
}
public static void main(String[] args) {
LRUCache lruCache = new LRUCache(2);
lruCache.put(1,1);
lruCache.put(2,2);
lruCache.get(1);
lruCache.put(3,3);
lruCache.get(2);
lruCache.put(4,4);
lruCache.get(1);
lruCache.get(3);
lruCache.get(4);
}
}
Java原始工具類來寫
class LRUCache {
private int capacity;
private LinkedHashMap<Integer, Integer> linkedHashMap;
public LRUCache(int capacity) {
this.capacity = capacity;
linkedHashMap = new LinkedHashMap<>();
}
public int get(int key) {
if (linkedHashMap.containsKey(key)) {
makeKeyRecent(key, linkedHashMap.get(key));
}
return linkedHashMap.getOrDefault(key, -1);
}
public void put(int key, int value) {
if (linkedHashMap.containsKey(key)) {
makeKeyRecent(key, value);
return;
}
if (linkedHashMap.size() == capacity) {
deleteOldestKey();
}
makeKeyRecent(key, value);
}
public void makeKeyRecent(int key, int val) {
linkedHashMap.remove(key);
linkedHashMap.put(key, val);
}
public void deleteOldestKey() {
/**
* 列表頭部就是最久未使用的
*/
int key = linkedHashMap.keySet().iterator().next();
linkedHashMap.remove(key);
}
}