手撕LRU 最近最少使用缓存淘汰策略 + LinkedHashMap

LRU 最近最少使用缓存淘汰策略

  • 1 LRU 算法就是一种缓存淘汰策略
  • 2 手撕LRU
  • 3 LinkedHashMap 常见面试题

1 LRU 算法就是一种缓存淘汰策略

计算机的缓存容量有限,如果缓存满了就要删除一些内容,给新内容腾位置。但问题是,删除哪些内容呢?我们肯定希望删掉哪些没什么用的缓存,而把有用的数据继续留在缓存里,方便之后继续使用。那么,什么样的数据,我们判定为「有用的」的数据呢?

LRU 缓存淘汰算法就是一种常用策略。LRU 的全称是 Least Recently Used,也就是说认为最近使用过的数据应该是是「有用的」,很久都没用过的数据应该是无用的,内存满了就优先删那些很久没用过的数据

ps:数据库采用改进的LRU 为了解决select *这种只使用一次,但是会把热数据:真正频率较高的挤到后面的问题。

2 手撕LRU

=========参考题解=========

设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构
实现LRUCache类:

  • LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存
  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
  • void put(int key, int value)
    • 如果关键字 key 已经存在,则变更其数据值 value
    • 如果不存在,则向缓存中插入该组 key-value
    • 如果插入操作导致关键字数量超过 capacity ,则应该 逐出 最久未使用的关键字

函数 get 和 put 必须以 O(1) 的平均时间复杂度运行。

示例:

输入:
[“LRUCache”, “put”, “put”, “get”, “put”, “get”, “put”, “get”, “get”, “get”]
[[2], [1, 1], [2, 2], [1], [3, 3], [2], [4, 4], [1], [3], [4]]

输出:
[null, null, null, 1, null, -1, null, -1, 3, 4]

解释:
LRUCache lRUCache = new LRUCache(2);
lRUCache.put(1, 1); // 缓存是 {1=1}
lRUCache.put(2, 2); // 缓存是 {1=1, 2=2}
lRUCache.get(1); // 返回 1
lRUCache.put(3, 3); // 该操作会使得关键字 2 作废,缓存是 {1=1, 3=3}
lRUCache.get(2); // 返回 -1 (未找到)
lRUCache.put(4, 4); // 该操作会使得关键字 1 作废,缓存是 {4=4, 3=3}
lRUCache.get(1); // 返回 -1 (未找到)
lRUCache.get(3); // 返回 3
lRUCache.get(4); // 返回 4

⭐️要点👿

  1. 定义一个双向链表:链表中的每个Node 包括:key value两个属性双向链表+dummy节点+形成一个环结构
    在这里插入图片描述
public static class Node{  //定义一个双向链表静态内部类 每个节点有key+value,还有prev指针 next指针(实现双向)int key;int value;Node prev;Node next;Node(int key, int value){ // 构造器this.key = key;this.value = value;}}
  1. 定义一个Hashmap:<key, Node>,为了方便根据 key 查找到对应的Node
    在这里插入图片描述
    capacity: 这是LRUCache类中存储缓存容量的变量,应该在对象创建后不可更改,因此使用 private final 来确保其值在对象创建后不会被修改。
    dummy 节点: 哨兵节点是双向链表中的一个特殊节点,它不存储任何实际的数据,仅用于简化链表操作。因为哨兵节点在整个对象生命周期中不会改变,所以将其声明为 private final
    myhashmap哈希表: 这是LRUCache类中存储键值对的哈希表,也应该在对象创建后不可更改。使用 private final 可以确保在对象创建后,哈希表的引用不会指向其他对象。
    总的来说,使用 private final 可以增强代码的安全性和稳定性,防止意外的修改或赋值操作,确保这些重要的成员变量在对象创建后保持不变。
private final int capacity; // 存储上限
private final Node dummy = new Node(0,0); // 新建一个dummy节点
private final HashMap<Integer, Node> myhashmap = new HashMap<>(); // 声明一个hashmap用于存储key-Node

  • int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。
    🔴 先去Hashmap中寻找有无存在key 【调用getNode(key)】
    a. 若有则返回对应的Node,再返回Node.value 。与此同时,由于操作了该节点,因此将该节点移动到链表头部。
    b. 若无则返回-1。

  • void put(int key, int value)
    - 如果关键字 key 已经存在,则变更其数据值 value ;
    - 如果不存在,则向缓存中插入该组 key-value 。
    - 如果插入操作导致关键字数量超过 capacity,则应该 逐出 最久未使用的关键字
    🔴先去Hashmap中寻找有无存在key 【调用getNode(key)】
    a. 若有则返回对应的Node,修改Node.value属性,即可实现更改值的目的。与此同时,由于操作了该节点,因此将该节点移动到链表头部。
    b. 如果没有,则新建一个Node节点包含key-Node,并将这个新建的节点放进链表头部。与此同时,在Hashmap中也添加这个<key,Node>

// 最近最少使用 用于页面置换算法,淘汰最近最少使用的页面
class LRUCache {public static class Node{  //定义一个双向链表静态内部类 每个节点有key+value,还有prev指针 next指针(实现双向)int key;int value;Node prev;Node next;Node(int key, int value){ // 构造器this.key = key;this.value = value;}}private final int capacity; // 存储上限private final Node dummy = new Node(0,0); // 新建一个dummy节点private final HashMap<Integer, Node> myhashmap = new HashMap<>(); // 声明一个hashmap用于存储key-Node// 【初始化】LRUCache的构造方法// 参数capacity用来指定LRU缓存的容量,即缓存可以存储的key-value对的数量上限public LRUCache(int capacity) { this.capacity = capacity;// 构造环 放在这里的原因:在Java中,类字段初始化必须在构造方法内或者直接初始化字段时进行,不能在类的主体外进行语句级别的初始化操作,所以不能把下面两行放到构造器外面。dummy.prev = dummy;dummy.next = dummy;}// 如果有对应的key,则返回key对应的value。如果没有就返回-1public int get(int key) {Node node = getNode(key); // 去尝试拿到这个nodeif(node != null) return node.value;else return -1;}// 如果key已经存在,则变更其数据值value // 如果key不存在,则向缓存中插入该组 key-value// 如果插入操作导致键值对数量超过 capacity ,则应该 逐出 最久未使用的关键字public void put(int key, int value) { Node node = getNode(key); // 去尝试拿到这个nodeif(node == null) { // 如果没拿到,即key不存在,则向缓存中插入该组 key-valueNode newNode = new Node(key,value);pushFront(newNode); // 添加到顶部myhashmap.put(key,newNode); // 添加到hashmapif(myhashmap.size() > capacity){ // 如果插入操作导致键值对数量超过 capacity ,则应该 逐出 最久未使用的关键字Node backNode = dummy.prev;remove(backNode); // 从链表移走最后一个节点myhashmap.remove(backNode.key); // 从hashmap移走最后一个节点对应的key-Node键值对}}else{ // 如果拿到,即key已经存在,则变更其数据值value node.value = value;}}// 【辅助方法】// 1.在hashmap中根据key拿到Nodeprivate Node getNode(int key){if(!myhashmap.containsKey(key)){ // 如果没有返回nullreturn null; }Node node = myhashmap.get(key); // 如果有就把这个Node找出来remove(node);  // 1.把他从原来的链表中删了pushFront(node); // 2.把他放到链表最开始return node;       // 3.返回这个节点}// 2.把节点从链表中删了 ——前后节点跳跃过node连接即可private void remove(Node node){node.prev.next = node.next;node.next.prev = node.prev;}// 3.把节点放到链表最开始private void pushFront(Node node){node.next = dummy.next;dummy.next.prev = node;node.prev = dummy;dummy.next = node;}
}/*** Your LRUCache object will be instantiated and called as such:* LRUCache obj = new LRUCache(capacity);* int param_1 = obj.get(key);* obj.put(key,value);*/

3 LinkedHashMap 常见面试题

LinkedHashMap 常见面试题
在这里插入图片描述

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

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

相关文章

react 祖孙关系传递

1. utils下的context.js import React ,{ createContext} from "react" //createContext 是创建一个上下文对象 export const dateContext createContext({}) // 创建了一个上下文对象 export const {Provider,Consumer} dateContext 2.爷爷组件 import {Provi…

【JavaScript 漫游】【041】File 对象、FileList 对象、FileReader 对象

文章简介 本篇文章为【JavaScript 漫游】专栏的第 041 篇文章&#xff0c;主要对浏览器模型中 File 对象、FileList 对象和 FileReader 对象的知识点进行了简记。 File 对象 File 对象代表一个文件&#xff0c;用来读写文件信息。它继承了 Blob 对象&#xff0c;或者说是一种…

适用于vue3的vant4组件 没有日期时间选择器

项目中需要用到日期和时间一同选择的场景 本来想用 如下代码 van-datetime-picker 发现咋整也不好使 刚开始还以为是引入的问题 后来发现是vant4根本就没这玩应了… <van-datetime-pickerv-model"currentDate"type"datetime"title"选择完整时间&q…

什么是Webhook 和 HTTP Endpoint?

Webhook 和 HTTP Endpoint 都是基于HTTP协议的网络通信概念&#xff0c;但它们在使用场景和目的上有所不同。 Webhook Webhook 是一种允许一个应用程序提供实时信息给其他应用程序的方法&#xff0c;这种通信是基于HTTP的“回调”或“钩子”。Webhook 通常被用来在一种服务上…

24届互联网秋招后端开发面经【Java\C++】(阿里、美团、快手等)

目录 写在前面阿里阿里数据库美团快手百度科大讯飞一面 字节一面 快手一面二面三面 滴滴1-3面部分米哈游一面 京东二面 美团二面三面 腾讯一面二面三面 高德一面二面三面 大文娱一面 蚂蚁二面 写在前面 博主24届985硕士非科班&#xff0c;秋招面了挺多大厂&#xff0c;拿了阿里…

如何通过Python脚本实现对远程物联网设备的监控和维护

通过Python脚本监控和维护远程物联网设备通常涉及以下几个步骤&#xff1a; 设备接入互联网&#xff1a;确保物联网设备能够连接到互联网&#xff0c;并有一个稳定的IP地址或者使用域名进行访问。选择通信协议&#xff1a;根据需求选择适合物联网设备的通信协议&#xff0c;如…

ETL的全量和增量模式

在当今信息爆炸的时代&#xff0c;数据管理已经成为各行各业必不可少的一环。而在数据管理中&#xff0c;全量与增量模式作为两种主要的策略&#xff0c;各自具有独特的优势和适用场景&#xff0c;巧妙地灵活运用二者不仅能提升数据处理效率&#xff0c;更能保障数据的准确性。…

JavaWeb -- HTTP -- WEB服务器TOMCAT

一.HTTP介绍: HTTP(Hyper Text Protocol) 实际上是一种超文本传输的协议,规定了浏览器跟服务器之间的一些数据传输的规则 例如B/S 对于浏览器的请求,以及相应服务器的响应,都必须依靠这种协议,规范,才能够彼此之间相互 理解 HTTP的协议特点: 1.基于TCP协议: 面向连接 更加安全…

一些常用的用法

Python的一些我没见过的用法&#xff0c;写一篇博客记录一下。 字典 kwargs 在 Python 中&#xff0c;kwargs 是一个通常用于表示关键字参数的字典。它通常在函数定义中用于接收传递给函数的任意数量的关键字参数。 当在函数调用时&#xff0c;使用关键字参数传递参数给函数…

golang kafka sarama 源码解析

消费者组重平衡 github.com/!shopify/saramav1.27.2/consumer_group.go func (c *consumerGroup) newSession(ctx context.Context, topics []string, handler ConsumerGroupHandler, retries int) (*consumerGroupSession, error) {// 获取broker组协调器coordinator, err :…

爱普生EPSON全新传感技术方案亮相高交会,创造新时代“精智生活”

2023年中国国际高新技术成果交易会在深圳福田会展中心盛大举行&#xff0c;是目前中国规模最大、最具影响力的科技类展会之一。爱普生作为始终坚持“科技本地化”战略的技术创新前沿企业参与此次展会&#xff0c;为中国用户带来爱普生电子元器件三款创新技术与四大成熟传感器解…

Python 将HTML转为PDF、图片、XML、XPS格式

网页内容是信息传播的主要形式之一。在Web开发中&#xff0c;有时候我们需要将HTML文件以不同的格式保存或分享&#xff0c;比如PDF、图片&#xff08;如PNG或JPEG&#xff09;、XML或XPS等。这些格式各有优势&#xff0c;适合不同的用途。在这篇文章中&#xff0c;我们将介绍如…

51单片机入门:定时器

定时器的介绍 定时器&#xff1a;51单片机的定时器属于单片机的内部资源&#xff0c;其电路的设计连接和运转均在单片机内部完成。根据单片机内部的时钟或者外部的脉冲信号对寄存器中的数据加1&#xff0c;定时器实质就是加1计数器。因为又可以定时又可以计数&#xff0c;又称…

Apipost智能Mock功能详解

在接口开发过程中&#xff0c;Mock功能可以帮助开发者快速测试和验证接口的正确性和稳定性&#xff0c;以便快速迭代和修复问题。Apipost推出智能Mock功能&#xff0c;可以在智能期望中填写一些触发条件&#xff0c;开启后&#xff0c;Apipost会根据已设置的触发条件&#xff0…

python食品安全信息管理系统flask-django-nodejs-php

。 食品安全信息管理系统是在安卓操作系统下的应用平台。为防止出现兼容性及稳定性问题&#xff0c;编辑器选择的是Hbuildex&#xff0c;安卓APP与后台服务端之间的数据存储主要通过MySQL。用户在使用应用时产生的数据通过 python等语言传递给数据库。通过此方式促进食品安全信…

Day43:WEB攻防-PHP应用SQL注入符号拼接请求方法HTTP头JSON编码类

目录 PHP-MYSQL-数据请求类型 PHP-MYSQL-数据请求方法 PHP-MYSQL-数据请求格式 知识点&#xff1a; 1、PHP-MYSQL-SQL注入-数据请求类型 2、PHP-MYSQL-SQL注入-数据请求方法 3、PHP-MYSQL-SQL注入-数据请求格式 PHP-MYSQL-数据请求类型 SQL语句由于在黑盒中是无法预知写法的…

大模型分布式推理ray

一、目录 1 框架 2. 入门 3. 安装教程 4. 相关文档、案例阅读 二、实现 1 框架&#xff1a;Ray&#xff1a;将一个模型拆分到多个显卡中&#xff0c;实现分布式预测、训练等功能。 2. 入门 &#xff1a; 案例&#xff1a;通过ray 实现分布式部署&#xff0c;分布式推理服务。…

python拍卖行系统的设计与实现flask-django-nodejs-php

此系统设计主要采用的是python语言来进行开发&#xff0c;采用django/flask框架技术&#xff0c;框架分为三层&#xff0c;分别是控制层Controller&#xff0c;业务处理层Service&#xff0c;持久层dao&#xff0c;能够采用多层次管理开发&#xff0c;对于各个模块设计制作有一…

排序算法记录(冒泡+快排+归并)

文章目录 前言冒泡排序快速排序归并排序 前言 冒泡 快排 归并&#xff0c;这三种排序算法太过经典&#xff0c;但又很容易忘了。虽然一开始接触雀氏这些算法雀氏有些头大&#xff0c;但时间长了也还好。主要是回忆这些算法干了啥很耗时间。 如果在笔试时要写一个o(nlogn)的…

408学习笔记-17-C-C/C++中程序内存区域划分

C/C中程序内存区域划分 C/C程序内存分配的几个区域&#xff1a; 1、栈区(stack)&#xff1a;在执行函数时&#xff0c;函数内局部变量的存储单元都可以在栈上创建&#xff0c;函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中&#xff0c;效率很高…