【数据结构与算法】通过双向链表和HashMap实现LRU缓存 详解

news2024/11/24 3:28:46

这个双向链表采用的是有伪头节点和伪尾节点的 与上一篇文章中单链表的实现不同,区别于在实例化这个链表时就初始化了的伪头节点和伪尾节点,并相互指向,在第一次添加节点时,不需要再考虑空指针指向问题了。

/**
 * 通过链表与HashMap实现LRU缓存
 *
 * @author CC
 * @version 1.0
 * @since2023/9/27
 */
public class LRUCache {


    private Map<Integer, Node> cache = new HashMap<>();//哈希表

    private int size;//链表长度

    private int capacity;//缓存容量

    private Node first;//伪头节点
    private Node last;//伪尾节点

    /**
     * 将一个新节点添加到头部
     *
     * @param newNode 要添加的新节点
     */
    private void addFirst(Node newNode) {
        //注意: 顺序很重要
        //1、分配新节点的前驱和后继
        newNode.prev = first;
        newNode.next = first.next;

        //2、头节点原来的后继的前驱指向新节点
        first.next.prev = newNode;
        //3、头节点的后继执行新节点
        first.next = newNode;

    }

    /**
     * 删除一个节点
     *
     * @param node 要删除的节点
     */
    private void deleteNode(Node node) {
        //要删除节点的后继和前驱相互指引
        node.prev.next = node.next;
        node.next.prev = node.prev;

    }

    /**
     * 将一个节点放到伪头节点后
     *
     * @param node 移动的节点
     */
    private void moveToFirst(Node node) {
        //删除这个节点
        deleteNode(node);
        //添加一个头节点
        addFirst(node);
    }

    /**
     * 删除尾节点
     *
     * @return 返回删除的这个节点
     */
    private Node deleteToLast() {
        //获得伪尾节点的前驱 也就是尾节点
        Node ret = last.prev;
        //删除尾节点
        deleteNode(last.prev);
        return ret;
    }

    /**
     * 存入缓存
     *
     * @param key
     * @param value
     */
    public void put(int key, int value) {
        //从hash表中查询这个健
        Node node = cache.get(key);

        //如果hash表中不存在要添加的健
        if (node == null) {
            //创建一个新的节点
            Node newNode = new Node(key, value);
            //将这个健和节点添加到hash表中
            cache.put(key, newNode);
            //将这个节点存到头节点中
            addFirst(newNode);
            //如果这个缓存已满
            if (++size > capacity) {
                //删除尾节点
                Node last = deleteToLast();
                //从hash表中也删除这个健
                cache.remove(last.key);
                size--;
            }
            //如果hash表中存在要添加的健
        } else {
            //将新添加的值覆盖原来的值
            node.value = value;
            //并移到头节点
            moveToFirst(node);
        }
    }

    /**
     * 获取缓存
     *
     * @param key 该缓存的健
     * @return 返回 该节点的值
     */
    public int get(int key) {
        //通过健从hash表中获取这个节点
        Node node = cache.get(key);
        //如果为空 则返回-1
        if (node == null) {
            return -1;
        }
        //否则 将该节点 移到头节点处
        moveToFirst(node);
        return node.value;
    }

    /**
     * 双向链表的遍历 头->尾
     *
     * @return
     */
    @Override
    public String toString() {
        StringJoiner sj = new StringJoiner("->");
        for (Node n =first.next;n.next!=null;n=n.next){
            sj.add(String.valueOf(n.value));
        }
        return "头->尾:"+sj.toString();
    }


    /**
     * 构造方法
     *
     * @param capacity 设置缓存容量
     */
    public LRUCache(int capacity) {
        size = 0;//初始链表长度位0
        this.capacity = capacity;//设置缓存容量

        first = new Node();//实例化伪头节点
        last = new Node();//实例化伪尾节点

        //初始头尾节点相互指向
        first.next = last;
        last.prev = first;
    }


    /**
     * 节点类
     */
    class Node {
        int key; //键
        int value;//值
        Node prev;//前驱
        Node next;//后继

        /**
         * 无参构造
         */
        public Node() {
        }

        /**
         * 有参构造
         *
         * @param key   健
         * @param value 值
         */
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
    }


}

测试

        //实例一个缓存大小为7的LRU缓存
        LRUCache lruCache =new LRUCache(5);
        lruCache.put(1,1);
        lruCache.put(2,2);
        lruCache.put(3,3);
        lruCache.put(4,4);
        lruCache.put(5,5);
        lruCache.put(6,6);

        System.out.println("依次存入1、2、3、4、5、6后的缓存:"+lruCache);
        int l1 = lruCache.get(1);
        System.out.println("取出1后的缓存:"+lruCache+",取出的值:"+l1);

        int l2 = lruCache.get(2);
        System.out.println("取出2后的缓存:"+lruCache+",取出的值:"+l2);

        int l3 = lruCache.get(3);
        System.out.println("取出3后的缓存:"+lruCache+",取出的值:"+l3);

        lruCache.put(9,9);
        System.out.println("存入9后的缓存:"+lruCache);

测试结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1049044.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

算法与数据结构-Trie树

文章目录 什么是“Trie 树”&#xff1f;如何实现一棵 Trie 树&#xff1f;Trie 树真的很耗内存吗&#xff1f;Trie 树与散列表、红黑树的比较 什么是“Trie 树”&#xff1f; Trie 树&#xff0c;也叫“字典树”。顾名思义&#xff0c;它是一个树形结构。它是一种专门处理字符…

SolidWorks 入门笔记03:生成工程图和一键标注

默认情况下&#xff0c;SOLIDWORKS系统在工程图和零件或装配体三维模型之间提供全相关的功能&#xff0c;全相关意味着无论什么时候修改零件或装配体的三维模型&#xff0c;所有相关的工程视图将自动更新&#xff0c;以反映零件或装配体的形状和尺寸变化&#xff1b;反之&#…

软考高级之系统架构师之计算机基础

概述 今天是9月28日&#xff0c;距离软考高级只剩37天&#xff0c;加油&#xff01; 概念 三种周期&#xff1a; Clock Cycle&#xff1a;时钟周期&#xff0c;CPU主频&#xff0c;又称为时钟频率&#xff0c;时钟周期是时钟频率的倒数Instruction Cycle&#xff1a;指令周…

人工智能 与 搜索引擎的较量

随着科技的不断进步&#xff0c;人工智能&#xff08;AI&#xff09;已经渗透到了我们生活的方方面面&#xff0c;搜索引擎也不例外。AI与传统搜索引擎之间的较量成为了科技界和互联网用户关注的热点话题。 人工智能 与 搜索引擎的较量 A - 搜索引擎B - 人工智能AI 的优势理解力…

感谢信 | 企企通赋能鲜丰水果搭建特色数字化供应链协同系统,领跑中国水果连锁品牌

近日&#xff0c;由企企通携手鲜丰水果股份有限公司&#xff08;以下简称“鲜丰水果”&#xff09;打造的一站式数字化采购供应链管理平台成功上线。 鲜丰水果项目顺利上线后&#xff0c;客户对企企通项目团队的努力付出作出了高度评价&#xff0c;并收到了来自鲜丰水果的感谢信…

Java中String转换为double类型

这次的java作业是写一个数字转换的小项目&#xff0c;其中从输入框中获取的是String类型&#xff0c;但是要进行数字操作&#xff0c;此时要用到很多操作String类型数据的方法了。 从javafx输入框中获取到String类型后&#xff0c;首先是要判断是否能转换为数字或者小数形式&a…

多线程 dispatch

51423 https://blog.csdn.net/yanhaijunyan/article/details/115083522

问题 - 谷歌浏览器 network 看不到接口请求解决方案

谷歌浏览器 -> 设置 -> 重置设置 -> 将设置还原为其默认值 查看接口情况&#xff0c;选择 All 或 Fetch/XHR&#xff0c;勾选 Has blocked cookies 即可 如果万一还不行&#xff0c;卸载浏览器重装。 参考&#xff1a;https://www.cnblogs.com/tully/p/16479528.html

WebGL 渲染三维图形作为纹理贴到另一个三维物体表面

目录 渲染到纹理 帧缓冲区对象和渲染缓冲区对象 帧缓冲区对象 帧缓冲区对象的结构 如何实现渲染到纹理 示例程序&#xff08;FramebufferObject.js&#xff09; 创建帧缓冲区对象&#xff08;gl.createFramebuffer&#xff08;&#xff09;&#xff09; gl.createFra…

windows:批处理bat入门

文章目录 什么是BAT常用命令与语法help与/?titlecolormodeechopausecallremset/a/p gotostartifif errorlevel for普通用法for /l 用法for /d用法for /r用法for /f用法in (file)delims和tokensskipeolusebackq 变量扩展变量延迟 setlocalshiftdirrd&#xff08;删除文件夹&…

云中网络的隔离GREVXLAN

底层的物理网络设备组成的网络我们称为 Underlay 网络&#xff0c;而用于虚拟机和云中的这些技术组成的网络称为 Overlay 网络&#xff0c;这是一种基于物理网络的虚拟化网络实现。 第一个技术是 GRE&#xff0c;全称 Generic Routing Encapsulation&#xff0c;它是一种 IP-o…

Xposed 替换Textview文案

Xposed 替换Textview文案 这篇主要写下替换Textview的文案&#xff0c;主要实现比如脱敏。 public class TextViewHook {private static final String TAG "TextViewHook";public static void hook(XC_LoadPackage.LoadPackageParam lpparam) {Log.i(TAG, "ho…

取得信息系统项目管理师证书,薪资待遇怎么样?

作为信息系统项目管理师&#xff0c;薪资待遇是大家关心的一个话题。在中国&#xff0c;信息系统项目管理师是一种相对新兴的职业&#xff0c;但随着信息化时代的到来&#xff0c;这个职业的需求也越来越大。那么&#xff0c;信息系统项目管理师的薪资待遇到底怎么样呢&#xf…

高防服务器给企业带来的优势有哪些?

高防服务器主要指的是能够提供给网络安全提供高防护的服务器&#xff0c;通过流量清洗、负载均衡等手段来抵御DDoS攻击、CC攻击这一类流量攻击&#xff0c;为企业提供了强大的数据保障&#xff0c;互联网时代数据安全是放在第一位的&#xff0c;数据泄漏的话不论对于企业还是对…

国庆周《Linux学习第二课》

Linux开篇指南针环境安装(第一课)-CSDN博客 Linux详细的环境安装介绍在上面 第一 环境准备过程 安装过程

PS与PL与PG082

参考&#xff08;照抄自己加点&#xff09;&#xff1a; ZYNQ PS-PL数据交互方式总结&#xff08;好文&#xff09;_axi emc-CSDN博客 zynq_process是一个用于方便操作PS和PL通信的GUI。 MIO分配在bank0和bank1直接与PS部分相连&#xff0c;EMIO分配在bank2直接和PL部分…

【每日一题】2703. 返回传递的参数的长度

2703. 返回传递的参数的长度 - 力扣&#xff08;LeetCode&#xff09; 请你编写一个函数 argumentsLength&#xff0c;返回传递给该函数的参数数量。 示例 1&#xff1a; 输入&#xff1a;args [5] 输出&#xff1a;1 解释&#xff1a; argumentsLength(5); // 1只传递了一个值…

odoo16 取消“系统各功能状态日报”的邮件

odoo16默认情况下每周都会发送一个“系统各功能状态日报”的邮件&#xff0c;而且是所有人都发&#xff0c; 这个功能在哪配置呢&#xff1f; 今天研究了一下&#xff0c; 线索是“系统各功能状态日报”&#xff0c;先全文检索吧 #. module: digest #: model:digest.digest,na…

windows:批处理bat实例

文章目录 文件/文件夹管理实例批量更改文件名创建编号从0到9的10个文件自动循环运行某个程序显示批处理的完整路径信息将文件名更名为当前系统日期使用批处理命令自动接收用户输入的信息计算当前目录及子目录&#xff08;中文件&#xff09;所占硬盘空间自动删除当前目录及子目…

【Spring Cloud系列】Config详解与应用

【Spring Cloud系列】Config详解与应用 文章目录 【Spring Cloud系列】Config详解与应用一、概述二、Config组成三、Spring Cloud Config 工作原理3.1 原理图3.2 Spring Cloud Config的原理 四、如何使用Spring Cloud Config4.1 创建Config Server4.2 创建Config Client4.3 配置…