简介
LFU(Least Frequently Used),即最不经常使用,淘汰一定时期内访问次数最少的元素,如果访问次数相同,则比较最新一次的访问时间。
代码实现1
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
public class LFUCache<K, V> {
private int capacity;
private Map<K, CacheObject<V>> cache;
public LFUCache(int capacity) {
this.capacity = capacity;
cache = new HashMap<>(capacity);
}
public V get(K key) {
CacheObject<V> cacheObject = cache.get(key);
if (cacheObject == null) {
return null;
}
return cacheObject.getValue();
}
public void put(K key, V value) {
if (capacity == 0) {
return;
}
CacheObject<V> cacheObject = cache.get(key);
if (cacheObject == null) {
if (cache.size() >= capacity) {
//删除最少使用的键,使用次数相等,删除最久未使用的
cache.entrySet().stream()
.min(Comparator.comparing(Entry::getValue))
.ifPresent(e -> {
cache.remove(e.getKey());
});
}
cacheObject = new CacheObject<>(value);
} else {
cacheObject.setValue(value);
}
cache.put(key, cacheObject);
}
@Override
public String toString() {
return cache.toString();
}
static class CacheObject<V> implements Comparable<CacheObject<V>> {
private V value;
private long lastAccessTime;
private int accessCount;
CacheObject(V value) {
setValue(value);
}
V getValue() {
lastAccessTime = System.nanoTime();
accessCount++;
return value;
}
public void setValue(V value) {
this.value = value;
lastAccessTime = System.nanoTime();
accessCount++;
}
@Override
public String toString() {
return value + ":" + accessCount;
}
@Override
public int compareTo(CacheObject<V> o) {
int res = Integer.compare(this.accessCount, o.accessCount);
return res != 0 ? res : Long.compare(this.lastAccessTime, o.lastAccessTime);
}
}
}
存储被缓存对象的访问次数和最后访问时间,当缓存已经达到最大容量时,删除最少访问次数的数据,如果两个数据访问次数相等,删除最后访问时间久的数据。
代码实现2
import java.util.HashMap;
import java.util.Map;
import java.util.TreeSet;
class LFUCache2<K, V> {
private int capacity;
private Map<K, CacheObject<K, V>> cache;
private TreeSet<CacheObject<K, V>> treeSet;
public LFUCache2(int capacity) {
this.capacity = capacity;
cache = new HashMap<>(capacity);
treeSet = new TreeSet<>();
}
public V get(K key) {
CacheObject<K, V> cacheObject = cache.get(key);
if (cacheObject == null) {
return null;
}
treeSet.remove(cacheObject);
V value = cacheObject.getValue();
treeSet.add(cacheObject);
return value;
}
public void put(K key, V value) {
if (capacity == 0) {
return;
}
CacheObject<K, V> cacheObject = cache.get(key);
if (cacheObject == null) {
if (cache.size() >= capacity) {
//删除最少使用的键,使用次数相等,删除最久未使用的
cache.remove(treeSet.first().key);
treeSet.pollFirst();
}
cacheObject = new CacheObject<>(key, value);
} else {
treeSet.remove(cacheObject);
cacheObject.setValue(value);
}
treeSet.add(cacheObject);
cache.put(key, cacheObject);
}
@Override
public String toString() {
return cache.toString();
}
static class CacheObject<K, V> implements Comparable<CacheObject<K, V>> {
private K key;
private V value;
private long lastAccessTime;
private int accessCount;
CacheObject(K key, V value) {
this.key = key;
setValue(value);
}
V getValue() {
lastAccessTime = System.nanoTime();
accessCount++;
return value;
}
public void setValue(V value) {
this.value = value;
lastAccessTime = System.nanoTime();
accessCount++;
}
@Override
public String toString() {
return value + ":" + accessCount;
}
@Override
public int compareTo(CacheObject<K, V> o) {
int res = Integer.compare(this.accessCount, o.accessCount);
return res != 0 ? res : Long.compare(lastAccessTime, o.lastAccessTime);
}
}
}
相比第一种,使用TreeSet存储访问次数的顺序关系,可以很快的得到最不经常使用的数据。