public class LFUCache {
private final Node first;
private final Node last;
private final HashRep cache;
private final HashMap<Integer,Node> count;
LFUCache(int capacity){
cache = new HashRep(capacity);
count = new HashMap<>();
first = new Node(0,0);
first.count=-1;
last = new Node(0,0);
last.count=-1;
first.next = last;
last.pre = first;
}
/**
* 首先,get并返回值
* 先从cache中get,如果为null,return -1
* 先看该节点在不在count中
* 如果在,删除掉该count
* count++
* 我们get一下这个++后的count值
* 如果有,move链表,设置count
*/
public int get(int key) {
if (cache.capacity<1){
return -1;
}
Node thisNode = cache.get(key);
if (thisNode==null){
return -1;
}
Node node = count.get(thisNode.count);
if (node.key==thisNode.key){
if (thisNode.next.count==thisNode.count){
count.put(thisNode.count,thisNode.next);
}else {
count.remove(thisNode.count);
}
}
thisNode.count++;
Node nextNode = count.get(thisNode.count);
if (nextNode!=null){
thisNode.move(nextNode);
}else if (node.key!=thisNode.key){
thisNode.move(node);
}
count.put(thisNode.count,thisNode);
return thisNode.val;
}
/**
* 1.工具类比较智能,new Node,直接put
* 2.返回0,只是修改了值,不做处理
* 3.返回1,添加成功,需要把他 放在 count[0] 前面,并把count[0]改成新节点
* 4.返回-1.很难受,我们要先删除最后一个节点
* cache 中删掉 remove方法
* 节点.del()删除链表中的引用关系
* 如果在count中,直接删除,因为它本来就在链表尾,不可能存在与他相同count
* 试图放在count[0] 前面,如果count[0]为空就放在last前面,更新count[0]
*/
public void put(int key, int value) {
if (cache.capacity<1){
return;
}
Node thisNode = new Node(key, value);
int sign = cache.put(thisNode);
if (sign == 0){
return;
}
if (sign==-1){
if (count.get(last.pre.count)==last.pre){
count.remove(last.pre.count);
}
cache.remove(last.pre.key);
last.pre.del();
}
Node nextNode = count.get(0);
if (nextNode==null){
nextNode=last;
}
thisNode.move(nextNode);
count.put(0,thisNode);
}
}
/**
* 这个类定义容器类,只维护节点的值,以及访问次数
* 不能维护任何链表顺序
* 实现LFU仍然需要一个映射来维护访问次数对应链表的位置
* 我们也可使用该工具类来保存映射 : 待定
*/
class HashRep {
final int capacity;
private int size;
private final int hashKey;
private final Node[] rep;
HashRep(int capacity) {
this.capacity = capacity;
size = 0;
int n = capacity - 1;
int p = 1;
while (p < 17) {
n |= n >>> p;
p <<= 1;
}
hashKey = n;
rep = new Node[hashKey + 1];
}
/**
* hash取index,循环取值,没取到返回-1,取到返回值
* 不对count做操作
* 单纯一个集合类
*/
Node get(int key) {
int index = key & hashKey;
Node aimNode = rep[index];
while (aimNode != null && aimNode.key != key) {
aimNode = aimNode.get;
}
return aimNode;
}
/**
* 返回0 存在key更改数值
* 返回1 添加
* 返回-1 添加,但容量溢出
*/
int put(Node node) {
int key = node.key;
int index = key & hashKey;
//三种情况: 1.index位置上为null 2. 往后找,找到了key 3.没有找到key相等的,取最后一个
Node aimNode = rep[index];
if (aimNode==null){
rep[index] = node;
}else {
if (aimNode.key==key){
aimNode.val = node.val;
return 0;
}
while(aimNode.get!=null){
//找到了key,更改值
if (aimNode.key==key){
aimNode.val = node.val;
return 0;
}
aimNode=aimNode.get;
}
aimNode.get = node;
}
size++;
return size>capacity?-1:1;
}
/**
* 如果在桶上面,桶下面没节点 直接删桶 size--
* 如果在桶上面,桶下面有节点,把桶下面那个移上来 size--
* 如果在桶下的节点之间,上面的get->这个的get size--
* 如果在桶末尾,上面那个节点指向这个节点的指针干掉 size--
*/
protected void remove(int key){
//一切为性能,不能遍历两边链表
int index = key & hashKey;
Node aimNode = rep[index];
if (aimNode==null){
return;
}
if (aimNode.key==key){
if (aimNode.get==null){
rep[index]=null;
size--;
}else{
rep[index] = aimNode.get;
size--;
}
return;
}
//注意,单向链表使用get来对比
while(aimNode.get!=null&&aimNode.get.key!=key){
aimNode = aimNode.get;
}
//aimNode.get 有两种情况 一种为null,一种为目标节点
if (aimNode.get!=null){
if (aimNode.get.get!=null){
aimNode.get = aimNode.get.get;
}else {
aimNode.get=null;
}
}
}
}
class Node {
Node pre;
Node next;
int key;
int val;
Node get;
int count=0;
Node(int key,int val){
this.key =key;
this.val = val;
}
public void move(Node nextNode){
if(this.next!=null){
this.next.pre = this.pre;
this.pre.next = this.next;
}
this.pre=nextNode.pre;
this.next=nextNode;
this.pre.next = this;
nextNode.pre = this;
}
public void del(){
this.pre.next = next;
this.next.pre = pre;
}
}
一些感想:
根据不同的需求选择什么样的缓存算法,以及创新新的缓存算法是以后学习的一个方向
但目前还是先把眼前的问题解决,在写较长的逻辑时总会有很多逻辑错误,发生错误总会加一些条件判断来避免同类错误
但是错误多了之后会使代码很乱,没有条理,更可怕的是加条件判断的时候没有经过思考,导致条件与逻辑冲突,加的判断还有新的错误等等...以后写东西一定将逻辑梳理完整再写,争取写出来一次就过..