LRU与LFU

一 概念

LRU:最近最少使用淘汰算法(Least Recently Used)。LRU是淘汰最长时间没有被使用的数据

LFU:最不经常使用淘汰算法(Least Frequently Used)。LFU是淘汰一段时间内,使用次数最少的数据

 

二 区别

LRU关键是看最后一次被使用到发生替换的时间长短,时间越长,就会被淘汰;而LFU关键是看一定时间段内被使用的频率(次数),使用频率越低,就会被淘汰

LRU算法适合:较大的文件比如游戏客户端(最近加载的地图文件)

LFU算法适合:较小的文件和教零碎的文件比如系统文件、应用程序文件

LRU消耗CPU资源较少,LFU消耗CPU资源较多

 

三实现

LRU实现:

  • 新数据插入到链表头部
  • 每当缓存命中(即缓存数据被访问),则将数据移到链表头部
  • 当链表满的时候,将链表尾部的数据丢弃

LRU具备的操作:

  • set(key, value):如果key在hashmap中存在,则先重置对应的value值,然后获取对应的节点cur,将cur节点从链表删除,并移动到链表的头部;若果key在hashmap不存在,则新建一个节点,并将节点放到链表的头部。当Cache存满的时候,将链表最后一个节点删除即可
  • get(key):如果key在hashmap中存在,则把对应的节点放到链表头部,并返回对应的value值;如果不存在,则返回-1

完整基于 Java 的代码参考如下

class DLinkedNode {
    String key;
    int value;
    DLinkedNode pre;
    DLinkedNode post;
}

LRU Cache

public class LRUCache {
   
    private Hashtable<Integer, DLinkedNode>
            cache = new Hashtable<Integer, DLinkedNode>();
    private int count;
    private int capacity;
    private DLinkedNode head, tail;

    public LRUCache(int capacity) {
        this.count = 0;
        this.capacity = capacity;

        head = new DLinkedNode();
        head.pre = null;

        tail = new DLinkedNode();
        tail.post = null;

        head.post = tail;
        tail.pre = head;
    }

    public int get(String key) {

        DLinkedNode node = cache.get(key);
        if(node == null){
            return -1; // should raise exception here.
        }

        // move the accessed node to the head;
        this.moveToHead(node);

        return node.value;
    }


    public void set(String key, int value) {
        DLinkedNode node = cache.get(key);

        if(node == null){

            DLinkedNode newNode = new DLinkedNode();
            newNode.key = key;
            newNode.value = value;

            this.cache.put(key, newNode);
            this.addNode(newNode);

            ++count;

            if(count > capacity){
                // pop the tail
                DLinkedNode tail = this.popTail();
                this.cache.remove(tail.key);
                --count;
            }
        }else{
            // update the value.
            node.value = value;
            this.moveToHead(node);
        }
    }
    /**
     * Always add the new node right after head;
     */
    private void addNode(DLinkedNode node){
        node.pre = head;
        node.post = head.post;

        head.post.pre = node;
        head.post = node;
    }

    /**
     * Remove an existing node from the linked list.
     */
    private void removeNode(DLinkedNode node){
        DLinkedNode pre = node.pre;
        DLinkedNode post = node.post;

        pre.post = post;
        post.pre = pre;
    }

    /**
     * Move certain node in between to the head.
     */
    private void moveToHead(DLinkedNode node){
        this.removeNode(node);
        this.addNode(node);
    }

    // pop the current tail.
    private DLinkedNode popTail(){
        DLinkedNode res = tail.pre;
        this.removeNode(res);
        return res;
    }
}

 

LFU具备的操作:

  • set(key, value):插入或修改缓存,如果key已存在,则将它对应的值改为value;如果key不存在,则插入键值对t(key, value),当缓存达到容量capacity时,则应该在插入新的键值对之前,删除使用频次最低的键值对。如果最低的键值对有多个,则删除其中最旧的那个
  • get(key):会去缓存中查询键key,如果key存在,则返回key对应的value,否则返回 -1

 完整基于 Java 的代码参考如下

class Node {
        int key;//
        int value;//
        int freq = 1;//频率
        //构造方法
        public Node() {}
        //构造方法
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }
class LFUCache1 {
        Map<Integer, Node> cacheMap;//存储缓存的内容
         Map<Integer,LinkedHashSet<Node>> freqMap; //存储每个频次对应的双向链表
         int size;
         int capacity;
         int min;//存储最小频次
         
        LFUCache1(int capacity) {
            cacheMap = new HashMap<Integer, p7.Node>(capacity);
            freqMap = new HashMap<Integer, LinkedHashSet<Node>>();
            this.capacity = capacity;
        }
        
        public int get(int key) {
            Node node = cacheMap.get(key);
            if(node == null) {
                return -1;
            }
            freqInc(node);//增加一次频次
            return node.value;
        }
        
        public void put(int key, int value) {
            if(capacity == 0) {
                return ;
            }
            Node node = cacheMap.get(key);
            if(node != null) {
                //已经存在了就替换掉,且增加一次频次
                node.value = value;
                freqInc(node);
            }else {
                if(size == capacity) {
                    //容量到达极限,移除
                    Node deadNode = removeNode();
                    cacheMap.remove(deadNode.key);
                    size--;
                }
                Node newnodeNode = new Node(key,value);
                cacheMap.put(key, newnodeNode);
                //没存在过,说明要增加最开始频率为1
                addNode(newnodeNode);
                size++;
            }
        }
        void freqInc(Node node) {
            //从原来对应的链表里面一处,并更新min;
            //获取这个节点的频率
            int freq = node.freq;
            //获得当前节点频率找到对应频率的HashMap里面的LinkedHashSet
            LinkedHashSet<Node> set = freqMap.get(freq);
            //移除掉LinkedHashSet里面的这个元素
            set.remove(node);
            if(freq == min && set.size() == 0) {
                min = freq +1;
            }
            //加入新freq对应的链表
            node.freq++;
            LinkedHashSet<Node> newSet = freqMap.get(freq+1);
            if(newSet == null) {
                newSet = new LinkedHashSet<>();
                freqMap.put(freq+1, newSet);
            }
            newSet.add(node);
        }
        
        void addNode(Node node) {
            LinkedHashSet<Node> set = freqMap.get(1);
            if( set == null) {
                set = new LinkedHashSet<p7.Node>();
                freqMap.put(1, set);
                
            }
            set.add(node);
            min = 1;
        }
        Node removeNode() {
            LinkedHashSet<Node> set = freqMap.get(min);
            Node deadNode = set.iterator().next();
            set.remove(deadNode);
            return deadNode;
        }
    }

 

posted on 2021-09-12 19:04  胡子就不刮  阅读(5697)  评论(0编辑  收藏  举报

导航