菜鸟笔记
提升您的技术认知

面试题

实现一个LRU Cache 算法LRU Cache C++三种解法java实现LRU算法及编码实现LRU策略缓存LRU算法常见缓存算法和LRU的c++实现设计循环双端队列(deque)LRU 缓存结构 (c++ 哈希双链表实现)LRU缓存机制删除单链表中的指定节点Linux 内核经典面试题拼多多社招面经:Redis是重点,讲一讲redis的内存模型线程、进程、协程的区别C++经典面试题面试官:我们只想要这样的C++工程师Linux C/C++ 学习路线链表操作汇总C++11的智能指针面试题浏览器中输入url后发生的事情常用的限流算法HTTP协议和HTTPS协议面试题网络编程面试题目总结c++后台面试题目如何实现LRU算法?如何寻找无序数组中的第K大元素?布隆过滤器 - 如何在100个亿URL中快速判断某URL是否存在?如何实现大整数相加?C++面试题及基本知识点总结C++给定出栈序列判定是否合法消息队列面试题要点redis缓存击穿,失效以及热点key解决方案网页在浏览器上的渲染过程几种限流算法lru算法例题C/C++常见面试知识点总结附面试真题----20210529更新引入MQ消息队列的作用及其优缺点MySQL面试篇社招三年后端面试题60道测开面试题,背完直接涨工资二叉树的层序遍历(两种方法实现)Bitmap 海量数据处理字符串倒序输出的五种方法C语言 输入10个数,统计出并输出正数、负数和0的个数字节三面:如何设计一个高并发系统架构,网络 面试36问,DDos攻击原理C++线程池使用 C++11 编写可复用多线程任务池LruCache结合Any容器类型实现任意类型缓存c++实现LFU算法

LruCache结合Any容器类型实现任意类型缓存

阅读 : 20

使用unordered_map 和 双向链表实现,支持设置缓存过期时间;

其中使用到的any.h 可查询我另一篇文章 任意类型容器


#ifndef _LRU_H_
#define _LRU_H_
#include <unordered_map>
#include <mutex>
#include <vector>
#include "any.h"

template<typename KEY, size_t AllocBatch = 16>
class LruCache {
public:
    struct Node {
        KEY     key;
        Any     val;
        time_t  expire;
        Node*   next;
        Node*   prev;
        template<typename VAL>
        void set(const KEY& k, const VAL& v, const int& e) {
            key = k;
            val = v;
            expire = e;
        }

        void init() {
            expire = 0;
            next = nullptr;
            prev = nullptr;
        }
    };
    explicit LruCache(const size_t& capacity = 100, bool pre_alloc = true): m_cap(capacity){
        m_head = nullptr;
        m_tail =  nullptr;

        if(pre_alloc) {
            m_available_node.resize(m_cap, nullptr);
            Node* nodes = (Node*)malloc(sizeof(Node) * m_cap);
            m_allocated.push_back(nodes);
            for(int i = 0; i < m_cap; i++) {
                m_available_node[i] = &nodes[i];
            }
        }
    }
    virtual ~LruCache(){
        for(void* mem : m_allocated) {
            free(mem);
        }
        m_allocated.clear();
    }
public:
    template<typename VAL>
    void Put(KEY key, VAL val, int expire = 0) {
        auto now = time(NULL);
        auto endTime = now + expire;
        if(expire == 0) {
            endTime = 0;
        }
        std::lock_guard<std::mutex> guard(m_mutex);
        if(m_map.count(key)) {
            m_map[key]->val = val;
            m_map[key]->expire = endTime;
            deleteNode(m_map[key]);
            insertHead(m_map[key]);
            return;
        }else if(m_map.size() >= Cap()) {
            Node* del = deleteNode(m_tail);
            m_available_node.push_back(del);
        }else if(m_available_node.empty()) {
            size_t alloc_size = Cap() - m_map.size();
            if(alloc_size > AllocBatch) {
                alloc_size = AllocBatch;
            }
            Node* nodes = (Node*)malloc(sizeof(Node) * alloc_size);
            m_allocated.push_back(nodes);
            for(size_t i = 0; i < alloc_size; i++) {
                m_available_node.push_back(&nodes[i]);
            }
        }

        Node* node = m_available_node.back();
        m_available_node.pop_back();
        node->init();
        node->set(key, val, endTime);
        m_map[key] = node;
        insertHead(node);

        // 每次插入,都检查尾部节点是否已经过期, 如果过期则立即删除
        checkTailExpire(now);
    }

    template<typename VAL>
    bool Get(KEY key, VAL& val) {
        std::lock_guard<std::mutex> guard(m_mutex);
        if(!m_map.count(key)) {
            return false;
        }
        auto now = time(NULL);
        if(m_map[key]->expire != 0 && m_map[key]->expire < now) {
            Node* del = deleteNode(m_map[key]);
            m_available_node.push_back(del); 
            m_map.erase(key);
            return false;
        }
        val = m_map[key]->val.template any_cast<VAL>();
        deleteNode(m_map[key]);
        insertHead(m_map[key]);

        // 每次操作都检查一下尾部节点是否已经过期,过期则删除,通常认为尾节点可能会最先过期
        checkTailExpire(now);
        return true;
    }

    bool Delete(KEY key) {
        std::lock_guard<std::mutex> guard(m_mutex);
        if(!m_map.count(key)) {
            return false;
        }
        deleteNode(m_map[key]);
        m_available_node.push_back(m_map[key]);
        m_map.erase(key);

        return true;
    }

    const size_t& Cap() const {
        return m_cap;
    }

    const size_t& Size() const {
        std::lock_guard<std::mutex> guard(m_mutex);
        return m_map.size();
    }
private:
    Node* deleteNode(Node* node) {
        if(node->prev) {
            node->prev->next = node->next;
        }
        if(node->next) {
            node->next->prev = node->prev;
        }
        if(node == m_head) {
            m_head = node->next;
        }
        if(node == m_tail) {
            m_tail = node->prev;
        }
        return node;
    }

    void insertHead(Node* node) {
        node->next = m_head;
        if(m_head) {
            m_head->prev = node;
        }
        node->prev = nullptr;
        m_head = node;
        if(m_tail == nullptr) {
            m_tail = node;
        }
    }

    void checkTailExpire(const time_t& now) {
        if(m_tail && m_tail->expire < now) {
            Node* del = deleteNode(m_tail);
            m_available_node.push_back(del);
        }
    }

private:
    const size_t                    m_cap;
    std::unordered_map<KEY, Node*>  m_map;
    struct Node*                    m_head;
    struct Node*                    m_tail;
    std::mutex                      m_mutex;
    std::vector<Node*>              m_available_node;   // 可用的Node节点池子,用于优化频繁的创建和释放Node节点
    std::vector<void*>              m_allocated;        // 被分配的首地址
};


#endif