title: 自己手写一个LRU策略

date: 2021-06-18 12:00:30

tags:

- [redis]

- [lru]

categories:

- [redis]

permalink: zxh

prefix: redis

一、题目描述

146. LRU 缓存机制

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制 。

实现 LRUCache 类:

LRUCache(int capacity) 以正整数作为容量 capacity 初始化LRU缓存

int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。

void put(int key, int value) 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字-值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。

进阶:你是否可以在 O(1) 时间复杂度内完成这两种操作?

二、思路分析

第一想法

  • 刚看到本题时没有多想就觉得会用到队列,因为队列FIFO可以做到淘汰末尾数据,但是仔细一想本题是需要淘汰最近最少使用数据,如果仅仅是最近的数据那么队列很容易实现。加上使用频率就涉及到数据的频繁挪动。很明显队列是无法完成的。
  • 那么有没有一种顺序添加的数据,每次在获取之后就会将数据前移至一端呢?答案是有的!LinkedHashMap
  • LinkedHashMap 不熟悉的朋友们可以简单的将它理解成HashMap 。 下图展示了HashMap的存储结构

  • 上述的元素我这里做了个动画演示全过程!!!

  • LinkedHashMap只是多了一条链表串起里面的元素

  • 这也是为什么LinkedHashMap是按照顺序存储的。但是LinkedHahsMap也无法做到按照使用频率进行排序啊?大家都知道他是按照添加顺序排序的!!!

LinkedHashMap改造

  • 原生的LinkedHashMap的确无法满足情况,但是我们稍微看下源码能够发现在put之后都会执行下afterNodeInsertion 这个方法。这也是HashMap留给LinkedHashMap做的扩展!

  • removeNode 就是将最前面的数据。想要进入这个方法就需要removeEldestEntry判断。LinkedHashMap默认是false . 所以我们只需要重写他就行了。但是还是在get值的时候如何保值在最后面呢?我们仔细看下源码就能够发现在get中有这个一个方法afterNodeAccess 。他的作用就是将get的元素移位值后面。正好符合我们LRU的策略特征

  • 综上!我们借助LinkedHashMap就非常容易的实现了LRU策略!

自己实现

  • 但是本题的意思是想考察我们自己是如何实现的,而不是巧妙对现有的工具改造的!不过上面对LinkedHashMap的确改造的很巧这是不可否认的!下面我们就尝试自己来实现下这种方式!

  • 首先我们需要确定需要用到Hash结合链表来实现。Hash我们自然使用HashMap来存储数据为的就是方便定位数据。定位到数据就需要操作链表将数据实时移位值链表尾部,每次淘汰是将链表首位移除既可。为了方便我们操作链表这里的链表肯定是双链表的!

链表单元

  • 首先我们定义一个内部类!用于链表的基本单元。里面存储了key,value方便根据Hash中存储的内容找到节点!preNodenextNode分别指向前后节点

  • 在构建器中初始化容量和链表大小,并初始化边界节点方便我们操作节点中移位和删除。

  • 在获取数据时没有添加就返回-1 , 已经添加的数据则将该数据对应的node节点移动到链表的尾部。

  • 在put中当第一次添加我们需要维护链表大小并进行检测是否需要进行淘汰数据,如果不是第一次添加我们只需奥更新值和对应node在链表中的位置即可

方法名 作用
addToTail 将节点添加值链表尾部
moveToTail 将已经存在于链表中的节点移动到链表的尾部
removeHeadNode 删除链表中第一个节点,注意是边界节点后第一个节点

四、总结

  • 虽然执行时间和内存消耗有点高!但是我就是不优化。
  • 本题主要就是在链表的移动上面会复杂点。我们需要按照添加顺序和使用频率两个维度进行维护他们之间的顺序。只要这个顺序维护好,就没啥问题了!

【redis前传】自己手写一个LRU策略 | redis淘汰策略的更多相关文章

  1. 搞定redis面试--Redis的过期策略?手写一个LRU?

    1 面试题 Redis的过期策略都有哪些?内存淘汰机制都有哪些?手写一下LRU代码实现? 2 考点分析 1)我往redis里写的数据怎么没了? 我们生产环境的redis怎么经常会丢掉一些数据?写进去了 ...

  2. 手写一个LRU工具类

    LRU概述 LRU算法,即最近最少使用算法.其使用场景非常广泛,像我们日常用的手机的后台应用展示,软件的复制粘贴板等. 本文将基于算法思想手写一个具有LRU算法功能的Java工具类. 结构设计 在插入 ...

  3. 面试题目:手写一个LRU算法实现

    一.常见的内存淘汰算法 FIFO  先进先出 在这种淘汰算法中,先进⼊缓存的会先被淘汰 命中率很低 LRU Least recently used,最近最少使⽤get 根据数据的历史访问记录来进⾏淘汰 ...

  4. 4.redis 的过期策略都有哪些?内存淘汰机制都有哪些?手写一下 LRU 代码实现?

    作者:中华石杉 面试题 redis 的过期策略都有哪些?内存淘汰机制都有哪些?手写一下 LRU 代码实现? 面试官心理分析 如果你连这个问题都不知道,上来就懵了,回答不出来,那线上你写代码的时候,想当 ...

  5. redis的过期策略都有哪些?内存淘汰机制都有哪些?手写一下LRU代码实现?

    redis的过期策略都有哪些? 设置过期时间: set key 的时候,使用expire time,就是过期时间.指定这个key比如说只能存活一个小时?10分钟?指定缓存到期就会失效. redis的过 ...

  6. 手把手教你手写一个最简单的 Spring Boot Starter

    欢迎关注微信公众号:「Java之言」技术文章持续更新,请持续关注...... 第一时间学习最新技术文章 领取最新技术学习资料视频 最新互联网资讯和面试经验 何为 Starter ? 想必大家都使用过 ...

  7. 只会用就out了,手写一个符合规范的Promise

    Promise是什么 所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果.从语法上说,Promise 是一个对象,从它可以获取异步操作的消息.Prom ...

  8. 利用SpringBoot+Logback手写一个简单的链路追踪

    目录 一.实现原理 二.代码实战 三.测试 最近线上排查问题时候,发现请求太多导致日志错综复杂,没办法把用户在一次或多次请求的日志关联在一起,所以就利用SpringBoot+Logback手写了一个简 ...

  9. 手写一个简单的ElasticSearch SQL转换器(一)

    一.前言 之前有个需求,是使ElasticSearch支持使用SQL进行简单查询,较新版本的ES已经支持该特性(不过貌似还是实验性质的?) ,而且git上也有elasticsearch-sql 插件, ...

随机推荐

  1. JUC 并发类概览

    JUC 并发类及并发相关类概览,持续补充... AQS 内部有两个队列,一个等待队列(前后节点),一个条件队列(后继节点),其实是通过链表方式实现: 等待队列是双向链表:条件队列是单向链表:条件队列如 ...

  2. Redis6.x学习笔记(二)持久化之RDB

    前言 最近学习Redis6.x,特做笔记以备忘,与大家共学.课程是从私塾在线下载的,他们把架构师课程都放出来了,大家可以去下载学习,不要钱的,地址是http://t.hk.uy/eK7,课程很不错,值 ...

  3. 消息队列RabbitMQ(三):消息确认机制

    引言 RabbitMQ的模型是生产者发送信息到 Broker (代理),消费者从 Broker 中取出信息.但是生产者怎么知道消息是否真的发送到 Broker 中了呢?Broker 又怎么知道消息到底 ...

  4. 阅读vue源码-----内置组件篇(keep-alive)

    1.前言: <keep-alive>是vue实现的一个内置组件,也就是说vue源码不仅实现了一套组件化的机制,也实现了一些内置组件. <keep-alive>官网介绍如下:&l ...

  5. [bug] Error updating database. Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MyS

    sql语句写错了,如图,where前多了个逗号

  6. [bug] sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1054, "Unknown column 'recevie_name' in 'field list'")

    Python Flask 开发购物网站,提交订单时报错 根据提示,检查代码,发现是字段名拼写错误导致,数据库对应的字段是receive_name,误写成了recevie_name 另外要注意,灰色字和 ...

  7. Docker Swarm(十)Portainer 集群可视化管理

    前言 搭建好我们的容器编排集群,那我们总不能日常的时候也在命令行进行操作,所以我们需要使用到一些可视化的工具,Docker图形化管理提供了很多工具,有Portainer.Docker UI.Shipy ...

  8. 攻防世界(十三)unserialize3

    攻防世界系列 :unserialize3 1.打开题目,反序列化 2.代码审计 类xctf被调用时_weakeup()函数会被自动执行,但当序列化字符串中属性值个数大于属性个数,就会导致反序列化异常, ...

  9. 配置yum仓库的三种方法光盘镜像、nginx、sftp

    方法一: 1.安装ftp服务 [root@oldboy ~]# yum -y install vsftpd 2.查看vsftpd相关的配置文件和目录 rpm -ql vsftpd # 查看vsftpd ...

  10. Elasticsearch + Logstash + Kibana +Redis +Filebeat 单机版日志收集环境搭建

    1.前置工作 1.虚拟机环境简介 Linux版本:Linux localhost.localdomain 3.10.0-862.el7.x86_64 #1 SMP Fri Apr 20 16:44:2 ...