什么是LFU?
当内存不足时,优先删除被操作次数最少的元素。
数据结构设计
哈希表 + 二维双向链表
二维链表维护不同访问次数和相同访问次数结点集合之间的关系。每一列的双向链表是所有出现操作次数相同的节点的集合,次数链表的头节点之间也相互连接的,同样构成双向链表。
二维链表的结构:
链表中Node结点的设计:
public static class Node {
Integer key;
Integer value;
Integer times;//记录被操作的次数
Node up;//记录上一个结点
Node down;//记录下一个结点
public Node(Integer key, Integer value, Integer times) {
this.key = key;
this.value = value;
this.times = times;
}
}
每一列的双向链表单独维护,每列之间也使用双向指针进行连接:
/**
* 相当于一个二维的双向链表,上下链表的是相同次数的结点,左右连接的是不同次数的结点
*/
public static class NodeList{
Node head;//当前列的第一个结点
Node tail;//当前列的最后一个结点
NodeList last;//左边的结点
NodeList next;//右边的结点
public NodeList(Node head) {
this.head = head;
this.tail = head;
}
//新增一个节点,放到NodeList的头部
public void addFromHead(Node node){
node.down = head;
head.up = node;
head = head.up;
}
public boolean isEmpty(){
return head == null;}
//删除指定的结点
public void deleteNode(Node node){
if (head == tail) {
head = null;
tail = null;
}else{
if (head == node) {
head = node.down;
head.up = null;
}else if (tail == node) {
tail = node.up;
tail.down = null;
}else{
node.up.down = node.down;
node.down.up = node.up;
}
}
//释放该结点的所有引用
node.up = null;
node.down = null;
}
}
LRU设计思路
当元素被操作(添加、访问…)后,将对应的Node的times++,然后被操作的结点从原来的列中删除,移到现在times所在的列。这样缓存中操作次数最少的元素就总是在第一列的第一个Node,当面临内存淘汰时就优先从头开始删除。
完整代码