LRU和LFU算法实现原理
前提
午休的时候刷了下技术文章,看到 LRU和LFU算法。大概看了下原理于是尝试重现一下。
一、LRU是什么
LRU是一种淘汰算法,全称是Least Recently Used。
二、LRU算法的思想
- 算法的思想就是:如果一个数据在最近一段时间没有被访问到,
那么在将来它被访问的可能性也很小。
所以,当指定的空间已存满数据时,应当把最久没有被访问到的数据淘汰。
二、LRU实现
- 根据算法思想实现需要满足已下条件
- 1.新增key变为最近使用,当空间已存满数据,删除不常访问的key。
- 2.获取值时快速找到某个key是否存在,存在key变为最近使用,并返回其对应的 value。
那么那种数据结构满足上面条件,查询快,插入,删除快? 双向链表+哈希表
内存中数据结构如下:
- 代码实现如下:
/**
* LRU全称Least Recently Used(最近最少使用),用来淘汰不常用数据,保留热点数据
*
*/
public class MyLRUCache {
private int capacity;
private int count;
private Node first;
private Node last;
private HashMap<String, Node> nodeHashMap;
public MyLRUCache(int capacity) {
this.nodeHashMap = new HashMap<>(capacity);
this.capacity = capacity;
Node firsNode = new Node();
firsNode.prev = null;
first = firsNode;
Node lastNode = new Node();
lastNode.next = null;
last = lastNode;
first.next = last;
last.prev = first;
}
public void set(String string, Object value) {
Node node = nodeHashMap.get(string);
if (node == null) {
Node newNode = new Node(string, value);
addFirst(newNode);
nodeHashMap.put(string, newNode);
++count;
if (count > capacity) {
//空间已满,删除不常访问的节点
Node last = this.last.prev;
this.nodeHashMap.remove(last.key);
this.removeNode(last);
--count;
}
} else {
node.value = value;
removeOldToAddFirst(node);
}
}
public Object get(String key) {
Node node = nodeHashMap.get(key);
if (node == null) {
return null;
}
// 将访问的节点移动到头节点
this.removeOldToAddFirst(node);
return node.value;
}
/**
* 删除节点
*
* @param node
*/
private void removeNode(Node node) {
Node pre = node.prev;
Node post = node.next;
pre.next = post;
post.prev = pre;
}
/**
* 删除指定节点并将该节点添加到头节点
*
* @param node
*/
private void removeOldToAddFirst(Node node) {
this.removeNode(node);
this.addFirst(node);
}
/**
* 在该列表开头插入指定的元素
*/
private void addFirst(Node node) {
node.prev = first;
node.next = first.next;
first.next.prev = node;
first.next = node;
}
@Data
static class Node {
private String key;
private Object value;
public Node(String key, Object value) {
this.key = key;
this.value = value;
}
public Node() {
}
//前驱节点
Node prev;
//后继点
Node next;
}
public String viewString() {
StringBuilder builder = new StringBuilder();
Node node = first.next;
for (; node != null; node = node.next) {
if (Objects.nonNull(node.value)) {
builder.append(node.value).append(" ");
}
}
return "MyLRUCache{" + builder.toString() +
'}';
}
public static void main(String[] args) {
MyLRUCache cache = new MyLRUCache(3);
cache.set("1", "1");
cache.set("2", "2");
cache.set("3", "3");
cache.set("4", "4");
System.out.println(cache.viewString()); //MyLRUCache{4 3 2 }
}
}
一、LFU是什么
LFU是一种淘汰算法(最近最少使用),全称是Least Frequently Used.
二、LFU算法的思想
- 算法的思想就是:如果一个数据在最近一段时间内访问次数很少,
那么在将来它被访问的可能性也很小。
所以,当指定的空间已存满数据时,应当把访问次数很少的数据淘汰。
这里可以看出LRU和LFU的区别一个是访问数据的顺序,一个是数据的访问次数
二、LFU实现
- 根据算法思想实现需要满足已下条件
- 1.新增key(默认访问次数1),当空间已存满数据,删除访问次数最少时间最长的key。
- 2.获取值时快速找到某个key是否存在,存在key(访问次数加+1),并返回其对应的 value。
- 3.多个key可能具有相同的访问次数
那么那种数据结构满足上面条件,查询快,插入,删除快?哈希表
内存中数据结构如下:
- 代码实现如下:
/**
* LFU是一种淘汰算法(最近最少使用),全称是Least Frequently Used.
*/
public class MyLFUCache {
private int capacity;
private int count;
private Integer freqMin;
private HashMap<String, Node> nodeHashMap;
private HashMap<Integer, LinkedList<Node>> freqHashMap;
public MyLFUCache(int capacity) {
this.capacity = capacity;
this.freqMin = 1;
int hashMapCapacity = new Double(Math.ceil(capacity / 0.75)).intValue() + 1;
this.nodeHashMap = new HashMap<>(hashMapCapacity);
this.freqHashMap = new HashMap<>(hashMapCapacity);
}
/**
* 新增node
*
* @param key
* @param value
*/
public void set(String key, Object value) {
Node node = nodeHashMap.get(key);
if (node == null) {
//更新最少访问频率
upDataFreqMin();
node = new Node(key, value);
addNode(node);
++count;
if (count > capacity) {
//容量已满淘汰数据
weedOut();
--count;
}
} else {
//覆盖旧值
node.setValue(value);
addNodeFreq(node);
}
}
private void weedOut() {
LinkedList<Node> linkedList = freqHashMap.get(freqMin);
nodeHashMap.remove(linkedList.getLast().getKey());
linkedList.removeLast();
}
/***
* 更新最少访问的频率
*/
private void upDataFreqMin() {
Set<Integer> keySet = freqHashMap.keySet();
if (keySet.size() > 0) {
this.freqMin = keySet.stream().reduce(Integer::min).get();
}
}
public Object get(String key) {
Node node = nodeHashMap.get(key);
if (node == null) {
return null;
}
addNodeFreq(node);
return node.getValue();
}
/**
* 删除节点
*/
private void removeNode(Node node) {
nodeHashMap.remove(node.getKey(), node);
LinkedList<Node> nodes = freqHashMap.get(node.freq);
nodes.remove(node);
if (nodes.size() == 0) {
freqHashMap.remove(node.freq);
}
}
private void addNode(Node node) {
nodeHashMap.put(node.getKey(), node);
LinkedList<Node> linkedList = freqHashMap.get(node.freq);
if (CollectionUtils.isEmpty(linkedList)) {
linkedList = new LinkedList<>();
freqHashMap.put(node.getFreq(), linkedList);
}
linkedList.addFirst(node);
}
private void addNodeFreq(Node node) {
removeNode(node);
node.setFreq(node.getFreq() + 1);
addNode(node);
upDataFreqMin();
}
@Data
static class Node {
private String key;
private Object value;
private Integer freq;
public Node(String key, Object value) {
this.key = key;
this.value = value;
this.freq = 1;
}
private Node() {
}
}
public void viewString() {
StringBuilder builder = new StringBuilder();
builder.append(" ");
List<Integer> list = new ArrayList<>(freqHashMap.keySet());
list.sort((a, b) -> {
if (a < b) {
return 1;
} else if (a > b) {
return -1;
}
return 0;
});
for (Integer integer : list) {
LinkedList<Node> nodes = freqHashMap.get(integer);
for (Node node : nodes) {
builder.append(node.key).append("=>").append(node.freq).append(" ");
}
}
System.out.println("MyLFUCache{" + builder.toString() +
'}');
}
public static void main(String[] args) {
MyLFUCache cache = new MyLFUCache(3);
cache.set("a", "a");
cache.set("a", "a");
cache.set("a", "a");
cache.set("b", "b");
cache.set("b", "b");
cache.set("c", "c");
cache.set("c", "c");
cache.set("d", "d");
cache.viewString(); //MyLFUCache{ a=>3 c=>2 d=>1 }
}
}
总结
完
感谢您的阅读
如果你发现了错误的地方,可以在留言区提出来,我对其加以修改