目录
一、LRU
1、什么是LRU
LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
2、如何实现
最常见的实现是使用一个链表保存缓存数据,详细算法实现如下:
1) 新数据插入到链表头部;
2) 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
3.)当链表满的时候,将链表尾部的数据丢弃。
LeetCode LRU缓存机制联系题目
https://leetcode-cn.com/problems/lru-cache/
3、分析
1)【命中率】
当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染情况比较严重。
2)【复杂度】
实现简单。
3)【代价】
命中时需要遍历链表,找到命中的数据块索引,然后需要将数据移到头部。
4、Java LinkedMap 实现
package com.sp7;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/*
* 为最近最少使用(LRU)缓存策略设计一个数据结构,
* 它应该支持以下操作:获取数据(get)和写入数据(set)。
* 获取数据get(key):如果缓存中存在key,则获取其数据值(通常是正数),否则返回-1。
* 写入数据set(key, value):如果key还没有在缓存中,则写入其数据值。
* 当缓存达到上限,它应该在写入新数据之前删除最近最少使用的数据用来腾出空闲位置。
* @author
*/
public class LruCache<K, V> {
LinkedHashMap<K,V> cache;
private int cacheSize;
public LruCache(final int cacheSize){
// ceil浮点数向上取整数
this.cacheSize = (int) Math.ceil (cacheSize / 0.75f) + 1;
//boolean accessOrder用来控制访问顺序的,,默认设置为false在访问之后,不会将当前访问的元素插入到链表尾部
cache = new LinkedHashMap<K,V>(this.cacheSize,0.75f,true){
//内部类来重写removeEldestEntry()方法
@Override
protected boolean removeEldestEntry (Map.Entry<K, V> eldest){
System.out.println("size="+size());
//当前size()大于了cacheSize便删掉头部的元素
return size() > cacheSize;
}
};
}
/**
* 如果使用继承的话就用getE而不是get,防止覆盖了父类的该方法
*/
public V get(K key){
return (V) cache.get(key);
}
public V set(K key,V value){
return cache.put(key, value);
}
public int getCacheSize() {
return cacheSize;
}
public void setCacheSize(int cacheSize) {
this.cacheSize = cacheSize;
}
public void printCache(){
for(Iterator it = cache.entrySet().iterator(); it.hasNext();){
Map.Entry<K,V> entry = (Map.Entry<K, V>)it.next();
if(!"".equals(entry.getValue())){
System.out.println(entry.getKey() + "\t" + entry.getValue());
}
}
System.out.println("------");
}
public void printLnCache(){
Set<Map.Entry<K,V>> set = cache.entrySet();
for(Map.Entry<K,V> entry : set){
K key = entry.getKey();
V value = entry.getValue();
System.out.println("key:"+key+"value:"+value);
}
}
public static void main(String[] args) {
LruCache<String,Integer> lrucache = new LruCache<String,Integer>(3);
lrucache.set("aaa", 1);
lrucache.printCache();
lrucache.set("bbb", 2);
lrucache.printCache();
lrucache.set("ccc", 3);
lrucache.printCache();
lrucache.set("ddd", 4);
lrucache.printCache();
lrucache.set("eee", 5);
lrucache.printCache();
System.out.println("这是访问了ddd后的结果");
lrucache.get("ddd");
lrucache.printCache();
lrucache.set("fff", 6);
lrucache.printCache();
lrucache.set("aaa", 7);
lrucache.printCache();
}
}
二、FIFO
什么是FIFO(First in first out)
FIFO按照“先进先出(First In,First Out)”的原理淘汰数据,正好符合队列的特性,数据结构上使用队列Queue来实现。
如下图:
1. 新访问的数据插入FIFO队列尾部,数据在FIFO队列中顺序移动;
2. 淘汰FIFO队列头部的数据;
三、LFU
LFU(Least Frequently Used)算法根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。
LFU的每个数据块都有一个引用计数,所有数据块按照引用计数排序,具有相同引用计数的数据块则按照时间排序。
具体实现如下:
1. 新加入数据插入到队列尾部(因为引用计数为1);
2. 队列中的数据被访问后,引用计数增加,队列重新排序;
3. 当需要淘汰数据时,将已经排序的列表最后的数据块删除。
LeetCode LFU缓存机制联系题目
https://leetcode-cn.com/problems/lfu-cache/
参考:
1、https://www.jianshu.com/p/b0ecc4fdb747
2、http://www.cnblogs.com/TenosDoIt/p/3417157.html
3、https://flychao88.iteye.com/blog/1977653
4、https://www.jianshu.com/p/d76a78086c3a
5、https://blog.csdn.net/alexander_xfl/article/details/12993565