LRUCache详解

2023-11-05

1.概念

LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法。
Cache的容量有限,因此当Cache的容量用完后,而又有新的内容需要添加进来时, 就需要挑选并舍弃原有的部分内容,从而腾出空间来放新内容。LRU Cache 的替换原则就是将最近最少使用的内容替换掉。其实,LRU译成最久未使用会更形象, 因为该算法每次替换掉的就是一段时间内最久没有使用过的内容。
 

2.LRUCache的实现原理

实现LRU Cache的方法和思路很多,但是要保持高效实现O(1)的put和get,那么使用双向链表和哈希表的搭配是最高效和经典的。

使用双向链表是因为双向链表可以实现任意位置O(1)的插入和删除,

使用哈希表是因为哈希表的增删查改也是O(1)。

package test;

import java.util.HashMap;
import java.util.Map;

class Node {
    public int key;
    public int val;

    public Node prev;

    public Node next;

    public Node() {

    }

    public Node(int key, int val) {
        this.key = key;
        this.val = val;
    }

    @Override
    public String toString() {
        return "Node{" +
                "key=" + key +
                ", val=" + val +
                '}';
    }
}

public class MyLRUCache {
    public Node head;
    public Node tail;

    public int usedsize;
    public Map<Integer, Node> cache;
    public int capacity;

    public MyLRUCache(int capacity) {
        this.head = new Node();
        this.tail = new Node();
        head.next = tail;
        tail.prev = head;
        cache = new HashMap<>();
        this.capacity = capacity;
    }

    //插入元素
    public void put(int key, int val) {
        Node node = cache.get(key);
        if (node == null) {
            Node newnode = new Node(key, val);
            cache.put(key, newnode);
            addToTail(newnode);
            usedsize++;
            //检查当前双向链表的有效数据个数 是不是超过了capacity
            if (usedsize > capacity) {
                Node removeNode = removeHead();
                cache.remove(removeNode.key);
                usedsize--;
            }
            printNodes("put");
        } else {
            //更新val的值,其实没必要
            node.val = val;
            moveTotail(node);
        }
    }

    //删除指定节点
    public void removeNode(Node node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    //移动当前节点到尾巴节点
    public void moveTotail(Node node) {
        removeNode(node);
        addToTail(node);
    }

    //添加到尾巴节点
    public void addToTail(Node node) {
        tail.prev.next = node;
        node.prev = tail.prev;
        node.next = tail;
        tail.prev = node;
    }

    //删除最近没使用的头结点
    public Node removeHead() {
        Node del = head.next;
        head.next = del.next;
        del.next.prev = head;
        return del;
    }

    //访问当前的key 把你访问的节点放到尾巴
    public int get(int key) {
        Node node = cache.get(key);
        if (node == null) {
            return -1;
        }
        //把最近使用的放到了尾巴
        moveTotail(node);
        printNodes("get");
        return node.val;
    }

    public void printNodes(String str) {
        System.out.println(str + " ");
        Node cur = head.next;
        while (cur != tail) {
            System.out.print(cur + " ");
            cur = cur.next;
        }
        System.out.println();
    }

    public static void main(String[] args) {
        MyLRUCache myLRUCache = new MyLRUCache(3);
        myLRUCache.put(100, 1);
        myLRUCache.put(200, 2);
        myLRUCache.put(300, 3);
        System.out.println("获取元素");
        myLRUCache.get(200);
        myLRUCache.get(100);
        System.out.println("存放元素,会删除头结点,因为头节点是最近最少使用的");
        myLRUCache.put(999, 9);
    }
}

3.jdk当中LRUCache被封装到了LinkedHashMap

package test;


import java.util.LinkedHashMap;
import java.util.Map;

public class LRUCache extends LinkedHashMap<String, Integer> {
    public int capacity;

    public LRUCache(int capacity) {
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }

    @Override
    public Integer get(Object key) {
        return super.getOrDefault(key, -1);
    }

    @Override
    public Integer put(String key, Integer value) {
        return super.put(key, value);
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) {
        return size() > capacity;
    }

    public static void main(String[] args) {
        LRUCache lruCache = new LRUCache(3);
        lruCache.put("a", 1);
        lruCache.put("b", 2);
        lruCache.put("c", 3);
        System.out.println(lruCache);
        System.out.println("获取元素");
        System.out.println(lruCache.get("b"));
        System.out.println(lruCache);
        System.out.println(lruCache.get("a"));
        System.out.println(lruCache);
        System.out.println("存放元素,会删除头结点,因为头结点是最近最少使用的");
        lruCache.put("d", 4);
        System.out.println(lruCache);
    }

    public static void main2(String[] args) {
        LinkedHashMap<String, Integer> linkedHashMap =
                new LinkedHashMap<String, Integer>(16, 0.7f, true);
        linkedHashMap.put("a", 1);
        linkedHashMap.put("b", 2);
        linkedHashMap.put("c", 3);
        System.out.println(linkedHashMap);
        System.out.println("获取元素");
        System.out.println(linkedHashMap.get("a"));
        System.out.println(linkedHashMap);
        System.out.println(linkedHashMap.get("b"));
        System.out.println(linkedHashMap);
        System.out.println(linkedHashMap.get("c"));
        System.out.println(linkedHashMap);//把常用的数据放到了尾巴 不常用的放在头部便于删除
    }

    public static void main1(String[] args) {
        LinkedHashMap<String, Integer> linkedHashMap =
                new LinkedHashMap<String, Integer>(16, 0.7f, false);
        linkedHashMap.put("a", 1);
        linkedHashMap.put("b", 2);
        linkedHashMap.put("c", 3);
        System.out.println(linkedHashMap);
        System.out.println("获取元素");
        System.out.println(linkedHashMap.get("a"));
        System.out.println(linkedHashMap);
    }
}

4.LRU缓存

 

class Node {
    public int key;
    public int val;

    public Node prev;

    public Node next;

    public Node() {

    }

    public Node(int key, int val) {
        this.key = key;
        this.val = val;
    }

    @Override
    public String toString() {
        return "Node{" +
                "key=" + key +
                ", val=" + val +
                '}';
    }
}

public class LRUCache {
    public Node head;
    public Node tail;

    public int usedsize;
    public Map<Integer, Node> cache;
    public int capacity;

    public LRUCache(int capacity) {
        this.head = new Node();
        this.tail = new Node();
        head.next = tail;
        tail.prev = head;
        cache = new HashMap<>();
        this.capacity = capacity;
    }

    //插入元素
    public void put(int key, int val) {
        Node node = cache.get(key);
        if (node == null) {
            Node newnode = new Node(key, val);
            cache.put(key, newnode);
            addToTail(newnode);
            usedsize++;
            //检查当前双向链表的有效数据个数 是不是超过了capacity
            if (usedsize > capacity) {
                Node removeNode = removeHead();
                cache.remove(removeNode.key);
                usedsize--;
            }
        } else {
            //更新val的值,其实没必要
            node.val = val;
            moveTotail(node);
        }
    }

    //删除指定节点
    public void removeNode(Node node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    //移动当前节点到尾巴节点
    public void moveTotail(Node node) {
        removeNode(node);
        addToTail(node);
    }

    //添加到尾巴节点
    public void addToTail(Node node) {
        tail.prev.next = node;
        node.prev = tail.prev;
        node.next = tail;
        tail.prev = node;
    }

    //删除最近没使用的头结点
    public Node removeHead() {
        Node del = head.next;
        head.next = del.next;
        del.next.prev = head;
        return del;
    }

    //访问当前的key 把你访问的节点放到尾巴
    public int get(int key) {
        Node node = cache.get(key);
        if (node == null) {
            return -1;
        }
        //把最近使用的放到了尾巴
        moveTotail(node);
        return node.val;
    }

    public void printNodes(String str) {
        System.out.println(str + " ");
        Node cur = head.next;
        while (cur != tail) {
            System.out.print(cur + " ");
            cur = cur.next;
        }
        System.out.println();
    }
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LRUCache详解 的相关文章

  • Pytorch—模型微调(fine-tune)

    随着深度学习的发展 在大模型的训练上都是在一些较大数据集上进行训练的 比如Imagenet 1k Imagenet 11k 甚至是ImageNet 21k等 但我们在实际应用中 我们自己的数据集可能比较小 只有几千张照片 这时从头训练具有几

随机推荐

  • fatal error: ceres/ceres.h: 没有那个文件或目录

    用ubuntu18跑的loam livox算法 系统报错 In file included from home lisheng catkin ws src loam livox master source laser mapping cpp
  • java面试---IO与NIO

    一 概念 NIO即New IO 这个库是在JDK1 4中才引入的 NIO和IO有相同的作用和目的 但实现方式不同 NIO主要用到的是块 所以NIO的效率要比IO高很多 在Java API中提供了两套NIO 一套是针对标准输入输出NIO 另一
  • springboot+mybatis配置多数据源实战

    1 背景说明 2 配置多数据源步骤 2 1 项目结构变更 2 2 添加配置类 2 3 修改配置文件数据连接配置信息 2 4 多数据源配置导致 Transactional失效问题 1 背景说明 一般一个项目中只会连接一个数据库 但是随着需求变
  • 后端配置(宝塔):处理php禁用函数

    一 找到php的文件路径 在软件商店中 找到已安装文件 选择需要更改的php文件 选择 设置 二 选择需要取消禁用的文件进行删除 扩展 可解决 The Process class relies on proc open which is n
  • vue常用指令和用法

    文章目录 1 v text 2 v html 3 v on 4 v show 5 v if 6 v bind 7 v for 8 v model 1 v text 设置标签的文本值内容 默然写法会替换全部内容 使用插值表达式 可以替换指定内
  • 题解:按钮加减计数器设计(单片机C51)(外部中断)

    需求 使用4位共阴极段码表及共阳极数码管 通过外部中断方式 实现两个按钮分配加1 减1功能 今天我就来讲解一下这道题 目录 1 代码 1 1定义头文件 1 2定义延时函数 毫秒 1 3定义主函数 1 4定义0 15共阴极数码管字符码 1 5
  • Linux 操作系统管理命令(全)

    目录 1 Linux常用命令 1 date 2 pwd命令 3 cd命令 4 cal命令 5 who命令 6 wc命令 7 uname命令 8 clear命令 9 logout命令 10 shutdown命令 2 命令高级操作 1 命令补全
  • VQ-VAE-2

    原文链接 Generating Diverse High Fidelity Images with VQ VAE 2 加载速度慢点这里 中科院镜像 由于科研需要 最近在学习图像生成相关的文献知识 VQ VAE 2是我目前了解到的比较新的生成
  • PMD检查java代码:为了提升性能,正确使用记录日志的语句(GuardLogStatement)

    https docs pmd code org pmd doc 6 55 0 pmd rules java bestpractices html guardlogstatement 对应记录日志的语句 要首先检查对应的日志级别有没有实际打开
  • Windows下VS2015编译caffe(CPU ONLY)

    本文参照了 Windows下VS2015编译caffe 零基础 1 环境 Windows 7VS2015 CPU ONLY 2 准备工作 原文说 https github com BVLC caffe tree windows Requir
  • linux验证cuda安装成功_CUDA9.1在Linux系统下runfile方式安装手册

    一 准备工作 确认是CUDA9 1 支持的Linux系统版本 3 确认gcc已安装 输入gcc version命令 如果有报错信息 需要重新安装gcc 4 确认安装了正确版本的kernel devel和kernel headers unam
  • 利用三轴加速度求解位移的算法—来自飞思卡尔方案

    在要求精度不高的情况 可以使用三轴加速度积分得到位移 飞思卡尔给出了官方方法 下文来自翻译说明 cache freescale com files senso 摘要 此文档描述并使用MMA7260QT三轴加速计和低功耗的9S08QG8八位单
  • Qt在connect中使用lambda表达式(最简单)

    若想在QT中使用lambda表达式需要在项目文件中的 pro 中加入 CONFIG c 11 例子 当点击按钮时 打印一个 输出 需要包含按钮类和打印调试类 include
  • ROS-Qt-转CMake编译以及qmake第三方库添加及其他

    Qt 开发ROS 界面的方法 方法2 带ui的工作空间配置 以ROS节点执行 步骤1 mkdir catkin qt cd catkin qt mkdir src cd src catkin init workpasce cd catkin
  • volatile足以保证数据同步吗

    在讨论之前必须先搞清四种存储介质 寄存器 高级缓存 RAM和ROM RAM与ROM大家都比较熟悉了 可以看成是我们经常说的内存与硬盘 寄存器属于处理器里面的一部分 而高级缓存cache是CPU设计者为提高性能引入的一个缓存 也可以说是属于处
  • axios post方式同时传递pram和json参数

    废话不多说 直接上代码 1 单独传递表单参数 后台使用 RequestParam接收 let postData mobile this account password this password loginType 0 let postD
  • 编译原理------语法分析器C/C++代码实现

    一 实验目的 编制一个递归下降分析程序 实现对词法分析程序所提供的单词序列的语法检查和结构分析 二 实验内容 利用C语言编制递归下降分析程序 并对简单语言进行语法分析 2 1 待分析的简单语言的语法 用扩充的BNF表示如下 lt 程序 gt
  • npm nrm安装后报错

    错误信息为 C Users Lenovo AppData Roaming npm node modules nrm cli js 9 const open require open Error ERR REQUIRE ESM require
  • Hadoop学习笔记(六)(Spark + Flink + Beam)

    spark 计算框架 速度 易用 通用性 Mapreduce是进程级别的 Spark是线程级别的 Spark生态系统 DBAS Berkeley Data Analytics Stack Mesos HDFS Tachyon 基于内存的文件
  • LRUCache详解

    1 概念 LRU是Least Recently Used的缩写 意思是最近最少使用 它是一种Cache替换算法 Cache的容量有限 因此当Cache的容量用完后 而又有新的内容需要添加进来时 就需要挑选并舍弃原有的部分内容 从而腾出空间来