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

面试题

实现一个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 编写可复用多线程任务池

java实现LRU算法及编码实现LRU策略缓存

阅读 : 1658

概念

  LRU(least recently used)就是将最近不被访问的数据给淘汰掉,LRU基于一种假设:认为最近使用过的数据将来被使用的概率也大,最近没有被访问的数据将来被使用的概率比较低。

原理

  LRU一般通过链表形式来存放缓存数据,新插入或被访问的数据放在链表头部,超过一定阈值后,自动淘汰链表尾部的数据。下图很形象的说明了LRU缓存淘汰过程。(图片来自网络)

LRU算法及编码实现

步骤:

1、新插入A, 将A放置在队列头部

2、新插入B, 将B放置在队列头部, A自动推举次席。

3、新插入C, 将C放置在队列头部, B自动推举次席。

4、新插入D, 将D放置在队列头部, C自动推举次席。

5、新插入E, 将E放置在队列头部, D自动推举次席。

6、新插入E, 将E放置在队列头部, 这时队列长度大于阈值,自动将尾部数据A给淘汰掉

7、访问数据C,然后将C重新放置在队列头部。

8、新插入E, 将E放置在队列头部, 这时队列长度大于阈值,自动淘汰尾部数据B

编码实现LRU策略缓存

/**
 * 2020/4/11.
 *
 * 使用链表+hashmap来实现, 这里没有考虑并发情况, 所以在代码中没有使用锁
 */
public class LRUCache<K, V> {

    class CacheNode {
        public CacheNode before;
        public Object key;
        public Object value;
        public CacheNode after;

        public CacheNode() {
        }
    }

    private HashMap<K, CacheNode> caches;
    private int maxCapacity;
    private int currentCacheSize;
    /**
     * 头结点, 头结点不参与淘汰,只是作为标识链表中的第一个节点
     */
    private CacheNode head;
    /**
     * 尾节点, 尾节点不参与淘汰, 只是作为标识链表中最后一个节点
     */
    private CacheNode tail;

    public LRUCache(int maxCapacity) {
        this.maxCapacity = maxCapacity;
        caches = new HashMap<>(maxCapacity);
        head = tail = new CacheNode();
        head.after = tail; //对head 的after节点赋值, 防止后续操作出现空指针异常
        tail.before = head; // 对tail的before节点赋值, 防止后续操作出现空指针异常
    }

    public void put(K k, V v) {
        CacheNode node = caches.get(k);
        if (node == null) {
            if (currentCacheSize >= maxCapacity) {
                caches.remove(tail.before.key); //淘汰尾部的元素
                removeLast();
            }

            node = new CacheNode();
            node.key = k;

            currentCacheSize ++;
        }

        node.value = v;
        moveToFirst(node); // LRU策略, 新插入的元素放置到队列头部
        caches.put(k, node);
    }

    public void moveToFirst(CacheNode node) {
        CacheNode n = head.after;
        head.after = node;
        node.before = head;
        n.before = node;
        node.after = n;
    }

    public void removeLast() {
        CacheNode n = tail.before.before;
        n.after = tail;
        tail.before = n;
    }

    public Object get(K k) {
        CacheNode node = caches.get(k);
        if (node == null) {
            return null;
        }

        Object v = node.value;
        moveToFirst(node);
        return v;
    }

    public Object remove(K k) {
        CacheNode node = caches.get(k);
        if (node == null) {
            return null;
        }

        CacheNode pre = node.before;
        CacheNode next = node.after;
        pre.after = next;
        next.before = pre;

        caches.remove(k);

        currentCacheSize --;
        return node.value;
    }
}