LRU缓存结构算法的核心就是哈希表和双向链表的结合。
[要求]:
- set和get方法的时间复杂度为O(1)
- 某个key的set或get操作一旦发生,认为这个key的记录成了最常使用的。
- 当缓存的大小超过K时,移除最不经常使用的记录,即set或get最久远的。
- 1.要保证set和get方法的时间复杂度为O(1),就是利用哈希表内存储的value实际上就是节点的地址,不用遍历取得。
- 2/3.实际上就是优先级的问题,使用双向链表节点的相对顺序标识优先级,链表头部节点优先级最低,尾部优先级最高,所以每次对节点操作之后都会把节点移动到尾部,以保证优先级。
操作步骤:
1.构造Node节点,设置为泛型,方便用户。
public static class Node<K, V> {
public K key;
public V value;
public Node last;
public Node next;
public Node(K key, V vaule) {
this.key = key;
this.value = vaule;
}
}
2.构建双向链表。
//双向链表
public static class NodeDoubleLinkedList<K, V> {
private Node<K, V> head;
private Node<K, V> tail;
// 构造函数,设置 head,tail = null
public NodeDoubleLinkedList() {
this.head = null;
this.tail = null;
}
// 加入Node节点到尾部
public void NodeAdd(Node<K, V> newNode) {
if (newNode == null) {
return;
}
if (this.head == null) {
this.head = newNode;
this.tail = newNode;
} else {
this.tail.next = newNode;
newNode.last = this.tail;
this.tail = newNode;
}
}
// 把节点移动到尾部,实际是把此节点的优先级调到最高
public void moveNodeToTail(Node<K, V> node) {
if (this.tail == null) {
return;
}
if (this.head == node) {
this.head = node.next;
node.next.last = null;
} else {
node.last.next = node.next;
node.next.last = node.last;
}
node.last = this.tail;
node.next = null;
this.tail.next = node;
this.tail = node;
}
// 移除头部Node,实际是把优先级最低的节点移除
public Node<K, V> removeHeadNode() {
if (this.head == null) {
return null;
}
Node<K, V> res = this.head;
if (this.head == this.tail) {
this.head = null;
this.tail = null;
} else {
this.head = res.next;
res.next = null;
this.head.last = null;
}
return res;
}
}
3. 构造缓存功能
// 缓存设置
public static class MyCache<K, V> {
private HashMap<K, Node<K, V>> keyNodeMap;
private NodeDoubleLinkedList<K, V> nodeList;
private int capacity; // 记录优先级
// 初始化优先级,哈希表,双向链表
public MyCache(int capacity) {
if (capacity < 1) {
throw new RuntimeException("should be more than 0.");
}
this.keyNodeMap = new HashMap<K, Node<K, V>>();
this.nodeList = new NodeDoubleLinkedList<K, V>();
this.capacity = capacity;
}
// 得到节点内的值并且把此节点移动到链表最后(优先级调到最高)
public V get(K key) {
if (this.keyNodeMap.containsKey(key)) {
Node<K, V> res = this.keyNodeMap.get(key);
this.nodeList.moveNodeToTail(res);
return res.value;
}
return null;
}
// 设置key,vaule.
public void set(K key, V value) {
// 如果哈希表内包含了这个key,代表更新操作
if (this.keyNodeMap.containsKey(key)) {
Node<K, V> node = this.keyNodeMap.get(key);
node.value = value;
this.nodeList.moveNodeToTail(node);
} else { // 新加记录
Node<K, V> newNode = new Node<K, V>(key, value);
this.keyNodeMap.put(key, newNode);
this.nodeList.NodeAdd(newNode);
if (this.keyNodeMap.size() == this.capacity + 1) {
this.removeMostUnusedCache();
}
}
}
// 移除头部Node,移除哈希表内key
private void removeMostUnusedCache() {
Node<K, V> removeNode = this.nodeList.removeHeadNode();
this.keyNodeMap.remove(removeNode.key);
}
}
4.主函数
public static void main(String[] args) {
// 构造缓存最大值为3的缓存表
MyCache<String, Integer> test = new MyCache<String, Integer>(3);
test.set("a", 1);
test.set("b", 2);
System.out.println(test.get("a"));
}
首次发文,不足之处请多多指点。