146.LRU缓存机制
思路
-
用HashMap类型的
cache
记录Cache
的数据 -
用List类型的
record
记录Cache
的各个元素使用情况record
有点类似队列,方便起见,下文就以队列称呼。- 对于cache中已有的元素: 若进行
get
put
操作,就将该元素移到队列(record)
的最后(即表示该数据最近使用过) - 对于新数据:先判断队列是否满
- 队列已满,删除第一个元素 ,剩下的元素往前移,再添加新元素,更新cache
- 队列未满,队尾添加新元素,更新cache
- 对于cache中已有的元素: 若进行
-
以上就是大致思路,同时,可以发现每次更新队列时,即元素的插入,移动等操作,效率其实是很低的,所有想起了链表,虽说链表插入数据的效率相对较高,可如何将元素移到尾部,效率还不如集合(数组)。以下代码肯定是需要优化的。
代码
//不能定义为静态变量 550ms
int capacity;
List<Integer> record=new ArrayList<>();
Map<Integer,Integer> cache=new HashMap<>();
public LRUCache(int capacity) {
this.capacity=capacity;
}
public int get(int key) {
if(cache.containsKey(key)){
int i=record.indexOf(key);
i++;
for(;i<record.size();i++){
record.set(i-1, record.get(i));
}
record.set(record.size()-1, key);
return cache.get(key);
}
return -1;
}
public void put(int key, int value) {
if(cache.size()<capacity){
if(cache.containsKey(key)){
int i = record.indexOf(key);
i++;
for(;i<record.size();i++){
record.set(i-1, record.get(i));
}
record.set(record.size()-1, key);
}else{
record.add(key);
}
cache.put(key, value);
}else{//cache已满
if(cache.containsKey(key)){
int i = record.indexOf(key);i++;
for(;i<record.size();i++){
record.set(i-1, record.get(i));
}
record.set(capacity-1, key);
cache.put(key, value);
}else{
//移除最久未使用的
cache.remove(record.get(0));
//更新队列
for(int i=1;i<record.size();i++){
record.set(i-1, record.get(i));
}
record.set(capacity-1, key);
cache.put(key, value);
}
}
}
链表+哈希表
代码
//使用链表
//19ms 80%
public class LRUCache2 {
class DLinkedNode{
int key;
int value;
DLinkedNode pre;
DLinkedNode next;
public DLinkedNode(){}
public DLinkedNode(int key,int value){this.key=key;this.value=value;}
}
private Map<Integer,DLinkedNode> cache=new HashMap<>();
private int size;
private int capacity;
//使用伪头部和伪尾部节点
private DLinkedNode head,tail;
public LRUCache2(int capacity){
this.size=0;
this.capacity=capacity;
head=new DLinkedNode();
tail=new DLinkedNode();
head.next=tail;
tail.pre=head;
}
public int get(int key){
DLinkedNode node=cache.get(key);
if(node==null) return -1;
//key存在,先从原位置移除 再移到头部
moveToHead(node);
return node.value;
}
public void put(int key,int value){
DLinkedNode node=cache.get(key);
if(node==null) {
//key不存在,创建新节点
DLinkedNode newNode=new DLinkedNode(key,value);
//加入哈希表
cache.put(key, newNode);
//添加到双向链表头部
addToHead(newNode);
size++;
//如果添加节点后 节点数量超过容量
if(size>capacity){
//移除尾部节点
DLinkedNode tail=removeTail();
//哈希表移除对应的项
cache.remove(tail.key);
size--;
}
}else{//key已存在,更新value值
node.value=value;
//再移至双向链表的头部
moveToHead(node);
}
}
private void addToHead(DLinkedNode node){
node.pre=head;
node.next=head.next;
head.next.pre=node;
head.next=node;
}
private void removeNode(DLinkedNode node){
node.pre.next=node.next;
node.next.pre=node.pre;
}
private void moveToHead(DLinkedNode node){
removeNode(node);
addToHead(node);
}
private DLinkedNode removeTail(){
DLinkedNode res=tail.pre;
removeNode(res);
return res;
}
}
参考链接: