java实现LRU缓存
LRU缓存: least recent used:最久不使用的淘汰;
特点:
- 加入或者更新将其移到最前面;
- 缓存不够了需要删除最久未使用的。
实现结构: 双链表
定义双链表节点:
/**
* 双链表的节点
*/
public class Node {
Object key;
Object value;
Node pre;
Node next;
public Node(Object key, Object value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return "{"+key+":"+value+"}";
}
}
Lru缓存实现
/**
* Lru缓存
* 不用就淘汰,用过就排在最前面
* 存不下的时候才淘汰
* 双链表:null--last--node1--node2--...node--first--null
* 初始状态 first=last=null
*/
public class LruCache<K,V> {
private int size;
private int currentSize;
private HashMap<K,Node> caches;
private Node first;//头节点
private Node last;//尾节点
//构造类,指定容量
public LruCache(int size){
currentSize=0;
this.size=size;
caches=new HashMap<K,Node>(size);
}
public String printCache(){
Node node=first;
String cacheStr="";
while (node!=null){
cacheStr+=node.toString()+"->";
node=node.next;
}
return cacheStr;
}
}
加入缓存
//加入缓存
public void put(K key,V value){
//如果是新元素,需要更新值
Node node=caches.get(key);
if(node==null){
//是否超过容量
if(caches.size()>=size){
remove((K) last.key);//移除
}
node=new Node(key,value);
caches.put(key,node);
}else{
node.value=value;
}
//将最新元素移动到头节点
moveToHead(node);
}
取缓存
//取缓存
public Object get(K key){
Node node=caches.get(key);
if(node==null){
return null;
}
moveToHead(node);
return node.value;
}
删除元素
/**
* 删除元素
* @param key
*/
private Object remove(K key){
Node node=caches.get(key);
if(node!=null){
if(node.pre!=null){
node.pre.next=node.next;
}
if(node.next!=null){
node.next.pre=node.pre;
}
if(node==first){
first=node.next;
}
if(node==last){
last=node.pre;
}
}
return caches.remove(key);
}
私有方法:将节点移动为头节点
/**
* 将某个节点移动到头节点,在添加节点/更新节点的时候执行:
* node.pre.next=node.next;
* node.next.pre=node.pre;//原链表重link
* node.next=first;first.pre=node;first=node;first.pre=null;
* @param node
*/
private void moveToHead(Node node){
if(first==node){
return;
}
if(node.next!=null){
node.next.pre=node.pre;
}
if(node.pre!=null){
node.pre.next=node.next;
}
if(node==last){
last=last.pre;
}
//空链表
if(first==null||last==null){
first=last=node;
return;
}
//更换头节点
node.next=first;
first.pre=node;
first=node;
first.pre=null;//是否有必要?
}
测试
public static void main(String[] args) {
LruCache<Integer,String> lru=new LruCache<Integer,String>(5);
lru.put(1,"a");
lru.put(2,"b");
lru.put(3,"c");
lru.put(4,"d");
lru.put(5,"e");
System.out.println("当前链表为"+lru.printCache());
lru.put(6,"F");//一般首先是溢出缓存
System.out.println("当前链表为"+lru.printCache());
System.out.println("获取元素"+lru.get(4)+",现在链表为:"+lru.printCache());
System.out.println("移除元素"+lru.remove(3)+",现在链表为:"+lru.printCache());
}
测试结果
当前链表为{5:e}->{4:d}->{3:c}->{2:b}->{1:a}->
当前链表为{6:F}->{5:e}->{4:d}->{3:c}->{2:b}->
获取元素d,现在链表为:{4:d}->{6:F}->{5:e}->{3:c}->{2:b}->
移除元素{3:c},现在链表为:{4:d}->{6:F}->{5:e}->{2:b}->