# LFU (最不经常使用缓存)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
import java.util.*;

public class LFUCache<K, V> {
private final int capacity;
private Map<K, V> cache;
private Map<K, Integer> freqMap;
private Map<Integer, LinkedHashSet<K>> freqKeysMap;
private int minFreq;

public LFUCache(int capacity) {
this.capacity = capacity;
cache = new HashMap<>();
freqMap = new HashMap<>();
freqKeysMap = new HashMap<>();
minFreq = 0;
}

public V get(K key) {
if (!cache.containsKey(key)) {
return null;
}
int freq = freqMap.get(key);
freqMap.put(key, freq + 1);
freqKeysMap.get(freq).remove(key);
if (freq == minFreq && freqKeysMap.get(freq).size() == 0) {
minFreq++;
}
if (!freqKeysMap.containsKey(freq + 1)) {
freqKeysMap.put(freq + 1, new LinkedHashSet<>());
}
freqKeysMap.get(freq + 1).add(key);
return cache.get(key);
}

public void put(K key, V value) {
if (capacity <= 0) {
return;
}
if (cache.containsKey(key)) {
cache.put(key, value);
get(key);
return;
}
if (cache.size() >= capacity) {
K evictKey = freqKeysMap.get(minFreq).iterator().next();
freqKeysMap.get(minFreq).remove(evictKey);
cache.remove(evictKey);
freqMap.remove(evictKey);
}
cache.put(key, value);
freqMap.put(key, 1);
minFreq = 1;
if (!freqKeysMap.containsKey(1)) {
freqKeysMap.put(1, new LinkedHashSet<>());
}
freqKeysMap.get(1).add(key);
}
}


LFU 缓存在处理缓存置换的时候会考虑到访问频率的因素。如果缓存空间已满,那么就要淘汰掉一些数据,以腾出空间存放新的数据。常见的淘汰算法有:先进先出(First In First Out,FIFO)、最近最少使用(Least Recently Used,LRU)和最不经常使用(Least Frequently Used,LFU)等。

这里我们实现了一个 LFU Cache,使用三个 Map 来存储缓存数据,缓存键的访问频率,以及不同访问频率下对应的缓存键集合。具体实现中,我们使用一个 minFreq 变量来记录当前最小访问频率,并在每次访问或插入数据时更新 minFreq。当缓存空间已满时,我们根据 minFreq 和缓存键集合中的顺序来选择要淘汰的数据。

该实现中,get 和 put 操作的时间复杂度均为 O (1)。如果需要支持高并发操作,可以在实现中加入线程安全机制。

更新于 阅读次数

请我喝[茶]~( ̄▽ ̄)~*

有李说不清 微信支付

微信支付

有李说不清 支付宝

支付宝

有李说不清 贝宝

贝宝