题目
运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。
- 获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。
- 写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。
- 当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。
进阶:
你是否可以在 O(1) 时间复杂度内完成这两种操作?
示例:
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
实现1:
import java.util.LinkedHashMap;
import java.util.HashMap;
import java.lang.IllegalArgumentException;
import java.util.Map;
public class LRUCache<K, V> extends LinkedHashMap<K, V>{
private int size;
public LRUCache(int size){
if(size <= 0){
throw new IllegalArguementException("invalid size");
}
super(size);
this.size = size;
}
@Override
public boolean removeEldestEntry(Map.Entry<K, V> entry){
return this.size() > size;
}
}
实现2:
package lru;
import java.util.HashMap;
import java.util.Map;
public class LruCache {
public static void main(String[] args) {
LruCache lru = new LruCache(3);
lru.put("a", "1");
lru.put("b", "2");
lru.put("c", "3");
lru.put("d", "4");
lru.put("f", "5");
System.out.println(lru.toString());
System.out.println();
}
private int capacity;
private Map<String, Node> key2Node;
private Node head;
private Node tail;
public LruCache(int capacity) {
if (capacity <= 0) {
throw new IllegalArgumentException("capacity can not le zero");
}
this.capacity = capacity;
this.key2Node = new HashMap<>(capacity);
this.head = this.tail = new Node();
}
public String get(String key) {
if (null == key) {
throw new IllegalArgumentException("key can not be null");
}
Node first = key2Node.get(key);
if (null == first) {
return null;
}
reputNode(first);
return first.val;
}
public void put(String key, String val) {
if (null == key || null == val) {
throw new IllegalArgumentException("key/val can not be null");
}
if (key2Node.containsKey(key)) {
//进行原节点替换
Node old = key2Node.get(key);
old.val = val;
reputNode(old);
} else {
if (key2Node.size() == this.capacity) {
key2Node.remove(tail.key);
moveTail();
}
Node newNode = new Node(key, val);
key2Node.put(key, newNode);
//如果当前没有节点,则作为尾结点加入
if (key2Node.size() == 1) {
tail = newNode;
}
addFirst(newNode);
}
}
private void reputNode(Node node) {
if (tail == node) {
moveTail();
}
addFirst(node);
}
/**
* 删除tail节点
*/
private void moveTail() {
//删除tail,将tail 指向 tail.pre
Node tailPre = tail.pre;
tail.pre = null;
if (null != tailPre) {
tailPre.next = null;
}
tail = tailPre;
}
private void addFirst(Node first) {
//链表移除first节点
Node pre = first.pre;
Node next = first.next;
if (null != pre) {
pre.next = next;
}
if (null != next) {
next.pre = pre;
}
//添加到首节点
Node second = head.next;
if (null != second) {
second.pre = first;
}
first.next = (second);
first.pre = head;
head.next = first;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
key2Node.entrySet().stream().forEach((entry ->
sb.append("[" + entry.getKey() + " , " + entry.getValue().val + "] ")
));
return "link: " + this.head.toString() + "\r\nmap:" + sb.toString() + "\r\ntail: " + (null != this.tail ? this.tail.toString() : "");
}
public static class Node {
String key;
String val;
Node next;
Node pre;
public Node() {
}
public Node(String key, String val) {
this.key = key;
this.val = val;
}
@Override
public String toString() {
return String.format("[key:%s, val:%s] -> ", this.key, this.val) + (null != this.next ? this.next.toString() : "");
}
}
}