缓存算法
1. LRU(Least Recently Used)
每次访问就把这个元素放到队列头部,队列满了淘汰队列尾的元素,也就是淘汰最长时间没有被访问的。
缺点:某一时刻大量数据的到来容易把热点数据挤出缓存,而这些数据却是只访问了一次的,今后不会再访问了的或者访问频率极低的。
java实现——LinkedHashMap
public class LRULinkedMap<K, V> {
/**
* 最大缓存大小
*/
private int cacheSize;
private LinkedHashMap<K, V> cacheMap;
public LRULinkedMap(int cacheSize){
this.cacheSize = cacheSize;
cacheMap = new LinkedHashMap(16, 0.75F, true){
@Override
protected boolean removeEldestEntry(Entry eldest) {
if(cacheSize + 1 == cacheMap.size()){
return true;
}else{
return false;
}
}
};
}
public void put(K key, V value){
cacheMap.put(key, value);
}
public V get(K key){
return cacheMap.get(key);
}
public Collection<Map.Entry<K, V>> getAll(){
return new ArrayList<Map.Entry<K, V>>(cacheMap.entrySet());
}
}
java实现——双向链表+hashMap
- 原理
这篇文章非常形象生动了,不啰嗦了:https://www.jianshu.com/p/74a4efacb0a7 - java实现
- 定义双向链表
class LRUNode {
Object key;
Object value;
LRUNode prev;
LRUNode next;
public LRUNode(Object key, Object value) {
this.key = key;
this.value = value;
}
}
- 实现缓存功能
class LRUCache{
private LRUNode head;
private LRUNode tail;
private final int capacity = 3;
HashMap<K, V> map = new HashMap<>();
public void set(K key, V value) {
LRUNode node = map.get(key);
if (node != null) {
node = map.get(key);
node.value = value;
remove(node, false);
} else {
node = new LRUNode(key, value);
if (map.size() >= capacity) {
// 每次容量不足时先删除最久未使用的元素
remove(tail, true);
}
map.put(key, node);
}
// 将刚添加的元素设置为head
setHead(node);
}
public Object get(K key) {
LRUNode node = map.get(key);
if (node != null) {
// 将刚操作的元素放到head
remove(node, false);
setHead(node);
return node.value;
}
return null;
}
private void setHead(LRUNode node) {
// 先从链表中删除该元素
if (head != null) {
node.next = head;
head.prev = node;
}
head = node;
if (tail == null) {
tail = node;
}
}
// 从链表中删除此Node,此时要注意该Node是head或者是tail的情形
private void remove(LRUNode node, boolean flag) {
if (node.prev != null) {
node.prev.next = node.next;
} else {
head = node.next;
}
if (node.next != null) {
node.next.prev = node.prev;
} else {
tail = node.prev;
}
node.next = null;
node.prev = null;
if (flag) {
map.remove(node.key);
}
}
}
2. LFU(Least Frequently Used)
淘汰一定时期内被访问次数最少的元素。如果元素的一定时间内的访问次数相同时,则比较他们的最新一次的访问时间
public class LFUAgingMap<K, V> extends HashMap<K, V> {
private static final int DEFAULT_MAX_SIZE = 3;
private int maxSize = DEFAULT_MAX_SIZE;
Map<K, HitRate> km = new HashMap<K, HitRate>();
public LFUAgingMap() {
this(DEFAULT_MAX_SIZE);
}
public LFUAgingMap(int maxSize) {
super(maxSize);
this.maxSize = maxSize;
}
@Override
public V get(Object key) {
V v = super.get(key);
if (v != null) {
HitRate hitRate = km.get(key);
hitRate.hitCount += 1;
hitRate.atime = System.nanoTime();
}
return v;
}
@Override
public V put(K key, V value) {
while (km.size() >= maxSize) {
K k = getLFUAging();
km.remove(k);
this.remove(k);
}
V v = super.put(key, value);
km.put(key, new HitRate(key, 1, System.nanoTime()));
return v;
}
private K getLFUAging() {
HitRate min = Collections.min(km.values());
return min.key;
}
// HitRate 命中率
class HitRate implements Comparable<HitRate> {
K key;
Integer hitCount; // 命中次数
Long atime; // 上次命中时间
public HitRate(K key, Integer hitCount, Long atime) {
this.key = key;
this.hitCount = hitCount;
this.atime = atime;
}
@Override
public int compareTo(HitRate o) {
int hr = hitCount.compareTo(o.hitCount);
return hr != 0 ? hr : atime.compareTo(o.atime);
}
}
}
Guava
原理后续学习
Caffeine
原理后续学习