LRU算法及Java实现

Java实现LRU(最近最少使用)算法 通过实现LRU算法,我们可以在有限的缓存容量下,有效地提高数据的命中率,减少不必要的数据访问开销。在Java中,我们可以选择使用双向链表和哈希表的组合,或者直接利用来实现LRU缓存。前者提供了更多的灵活性和控制能力,而后者则更加简洁和高效。根据具体的应用场景和需求,我们可以选择合适的实现方式。版权声明:本博客内容为原创,转载请保留原文链接及作者信息。 阅读详情

LRU算法简介

LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。

方法一,使用hashmap

  1. 在O(1)时间内操作对应的key|value,应该考虑到使用hashmap;
  2. 每当缓存命中(即缓存数据被访问),则将数据移到链表头部;
  3. 当链表满的时候,将链表尾部的数据丢弃。
//用HashMap和一个双向链表来实现LRU缓存

class LRUCache {

    // 双向链表节点定义
    class Node {
        int key;
        int val;
        Node prev;
        Node next;
    }
    
    private int capacity;
    //保存链表的头节点和尾节点
    private Node first;
    private Node last;
    
    private Map<Integer, Node> map;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new HashMap<>(capacity);
    }
    
    public int get(int key) {
        Node node = map.get(key);
        //为空返回-1
        if (node == null) {
            return -1;
        }
        moveToHead(node);
        return node.val;
    }
    
    private void moveToHead(Node node) {
        if (node == first) {
            return;
        } else if (node == last) {
            last.prev.next = null;
            last = last.prev;
        } else {
            node.prev.next = node.next;
            node.next.prev = node.prev;
        }
        
        node.prev = first.prev;
        node.next = first;
        first.prev = node;
        first = node;
    }
    
    public void put(int key, int value) {
        Node node = map.get(key);
        
        if (node == null) {
            node = new Node();
            node.key = key;
            node.val = value;
            
            if(map.size() == capacity) {
                removeLast();
            }
            
            addToHead(node);
            map.put(key, node);
        } else {
            node.val = value;
            moveToHead(node);
        }
    }
    
    private void addToHead(Node node) {
        if (map.isEmpty()) {
            first = node;
            last = node;
        } else {
            node.next = first;
            first.prev = node;
            first = node;
        }
    }
    
    private void removeLast() {
        map.remove(last.key);
        Node prevNode = last.prev;
        if (prevNode != null) {
            prevNode.next = null;
            last = prevNode;
        }
    }
    
    @Override
    public String toString() {
    	return map.keySet().toString();
    }
    
    public static void main(String[] args) {
    	LRUCache cache = new LRUCache(3);
        cache.put(1, 1);
        cache.put(2, 2);
        cache.put(3, 3);
        cache.get(1);
        cache.put(4, 3);
        System.out.println(cache);
	}
}

方法二,使用linkedhashmap

其实JDK已经为我们提供了一个基于HashMap和双向链表实现的数据结构,它就是LinkedHashMap,它内部维护的双向链表,可以帮助我们维护两种顺序:

  • 插入顺序
  • LRU顺序

下面简单的介绍下LinkedHashMap

public class LinkedHashMap<K,V> extends HashMap<K,V> implements Map<K,V>

它继承自HashMap,同样具有快速查找特性。

/**
 * The head (eldest) of the doubly linked list.
 */
transient LinkedHashMap.Entry<K,V> head;

/**
 * The tail (youngest) of the doubly linked list.
 */
transient LinkedHashMap.Entry<K,V> tail;

内部维护了一个双向链表,用来维护插入顺序或者 LRU 顺序。
另外根据注释,我们可以看出来,头节点head是最老的节点,而尾节点是最近一次被访问的节点,所以维护LRU顺序时,容量用完后,再执行新数据的插入的话,会移除头节点。

final boolean accessOrder;

accessOrder决定了维护的是哪一种顺序,默认为 false,此时维护的是插入顺序。为true时,维护的就是LRU顺序了。

void afterNodeAccess(Node<K,V> p) { }
void afterNodeInsertion(boolean evict) { }

LinkedHashMap 最重要的是上述这两个用于维护顺序的函数,它们会在 put、get 等方法中调用。

void afterNodeAccess(Node<K,V> e) { // move node to last
    LinkedHashMap.Entry<K,V> last;
    if (accessOrder && (last = tail) != e) {
        LinkedHashMap.Entry<K,V> p =
            (LinkedHashMap.Entry<K,V>)e, b = p.before, a = p.after;
        p.after = null;
        if (b == null)
            head = a;
        else
            b.after = a;
        if (a != null)
            a.before = b;
        else
            last = b;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
        tail = p;
        ++modCount;
    }
}

当一个节点被访问时,如果 accessOrder 为 true,则会将该节点移到链表尾部。也就是说指定为 LRU 顺序之后,在每次访问一个节点时,会将这个节点移到链表尾部,保证链表尾部是最近访问的节点,那么链表首部就是最近最少使用的节点。

void afterNodeInsertion(boolean evict) { // possibly remove eldest
    LinkedHashMap.Entry<K,V> first;
    if (evict && (first = head) != null && removeEldestEntry(first)) {
        K key = first.key;
        removeNode(hash(key), key, null, false, true);
    }
}

在 put 等操作之后执行,当 removeEldestEntry() 方法返回 true 时会移除最老的节点,也就是链表首部节点 first。

protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
    return false;
}

removeEldestEntry() 默认返回 false,如果需要让它为 true,需要继承 LinkedHashMap 并且重写这个方法,这是实现 LRU 缓存的关键,通过移除最近最少使用的节点,从而保证缓存空间足够,并且缓存的数据都是热点数据。

接下来我们基于JDK提供给我们的LinkedHashMap来实现LRU缓存

public class LRUCache<K,V> extends LinkedHashMap<K, V>{
	
	//首先设定最大缓存空间 MAX_ENTRIES 为 3;
	private static final int MAX_ENTRIES = 3;
	
	//之后使用LinkedHashMap的构造函数将 accessOrder设置为 true,开启 LRU顺序;
	public LRUCache() {
		super(MAX_ENTRIES, 0.75f, true);
	}
	
	//最后覆盖removeEldestEntry()方法实现,在节点多于 MAX_ENTRIES 就会将最近最少使用的数据移除。
	//因为这个函数默认返回false,不重写的话缓存爆了的时候无法删除最近最久未使用的节点
	@Override
	protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
		//在容量超过最大允许节点数的时候返回true,使得在afterNodeInsertion函数中能执行removeNode()
		return size() > MAX_ENTRIES;
	}
	
	public static void main(String[] args) {
	    LRUCache<Integer, Integer> cache = new LRUCache<>();
	    cache.put(1, 1);
	    cache.put(2, 2);
	    cache.put(3, 3);
	    cache.get(1);
	    cache.put(4, 4);
	    System.out.println(cache.keySet());
	}
}

打印结果

[3, 1, 4]
Java实现一个LRU算法 LRU缓存淘汰策略 LRU(最近最少使用)是一种高效的缓存淘汰算法,其核心思想是优先淘汰最久未被访问的数据。该策略通过哈希表和双向链表实现:哈希表提供O(1)时间复杂度的数据查找,双向链表维护数据访问顺序(头部存最新访问数据,尾部存最久未访问数据)。当缓存满时,直接删除链表尾部节点即可。这种设计既保证了快速访问,又能高效维护数据的时效性,广泛应用于数据库缓存、页面置换等场景。 阅读详情

相关推荐

Java实现代码LRU算法

LRU算法 + Java实现代码 LRU原理 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。 最常见的实现是使用一个链表保存缓存数据,详细算法实现如下 新数据插入到链表头部; 每当缓存命中(即缓存数据被访问),则将数据移到链表头部; 当链表满的时候,将链表尾部的数据丢弃。 【命中率】 当存在热点数据时,LRU的效率很好,但偶发性的、周期性的批量操作会导致LRU命中率急剧下降,缓存污染

xingdianp的博客 441

LRU(JAVA简洁版)

一、算法介绍 最近最久未使用(Least Recently Used LRU)算法是⼀种缓存淘汰策略,它是大部分操作系统为最大化页面命中率而广泛采用的一种页面置换算法。该算法的思路是,发生缺页中断时,将最近一段时间内最久未使用的页面置换出去。 从程序运行的原理来看,最近最久未使用算法是比较接近理想的一种页面置换算法,这种算法既充分利用了内存中页面调用的历史信息,又正确反映了程序的局部问题。 虚拟页式存储管理,则是将进程所需空间划分为多个页面,内存中只存放当前所需页面,其余页面放入外存的管理方式。 ..

上山的人 1万+

LRU算法 + Java实现代码

LRU原理 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。 最常见的实现是使用一个链表保存缓存数据,详细算法实现如下 新数据插入到链表头部; 每当缓存命中(即缓存数据被访问),则将数据移到链表头部; 当链表满的时候,将链表尾部的数据丢弃。 【命中率】 当存在热...

Mr_HHH的博客 885

java实现LRU缓存淘汰算法

LRU算法:最近最少使用淘汰算法(Least Recently Used)。LRU是淘汰最长时间没有被使用的缓存(即使该缓存被访问的次数最多)。 代码如下: import java.util.*; public class LRUCache { int cap;//最大缓存的数量 Map<String, String> values;//缓存 Set<String> position;//缓存的key,按照存入的顺序存储 public LRUCa

西凉的悲伤博客 3423

基于Java实现LRU算法

目录 一、什么是LRU 二、 选用LinkedHashMap实现 一、什么是LRU LRU是指最近最少使用,是一种缓存淘汰算法。根据数据的历史访问记录来进行淘汰数据,把最近一次使用时间离现在时间最远的数据删除掉,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。 以下摘自百度百科: LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问...

Harrison 1776

Java实战-基于JDK的LRU算法实现、优雅的实现代码耗时统计(Spring AOP、AutoCloseable方式)

LRU算法-缓存淘汰算法-Least recently used,最近最少使用算法根据数据的历史访问记录来进行淘汰数据,其核心思想是:如果有数据最近被访问过,那么将来被访问的几率也更高在Java中可以利用LinkedHashMap容器简单实现LRU算法LinkedHashMap底层就是用的HashMap加双链表实现的,而且本身已经实现了按照访问顺序的存储。此外,LinkedHashMap中本身就实现了一个方法removeEldestEntry用于判断是否需要移除最不常读取的数,

BADAO_LIUMANG_QIZHI的博客 679

java中LRU算法

当我们处理大量数据时,经常需要使用缓存来提高性能。LRU(最近最少使用)算法是一种常用的缓存淘汰策略,用于在缓存空间不足时选择要删除的数据。首先,我们需要了解LRU算法的工作原理。LRU算法的核心思想是基于数据的访问模式:最近被访问的数据很可能在不久的将来再次被访问,而最久未被访问的数据可能很快就会被废弃。LRU算法基于这个思想,将最近访问的数据保留在缓存中,而淘汰最久未被访问的数据。除了使用来实现LRU算法外,我们还可以通过自定义数据结构来实现。

m0_68642284的博客 1488

Alphafold2/multimer本地安装避坑指南:从Conda环境配置到GPU加速实战

本文详细介绍了Alphafold2/multimer本地安装的全流程,从Conda环境配置到GPU加速实战,特别针对常见的报错处理提供了解决方案。通过硬件环境检查、依赖安装技巧、OpenMM补丁应用等步骤,帮助研究人员高效部署蛋白质结构预测工具,提升计算效率。

weixin_29309213的博客 116

LRU算法(java实现)

Java实现LRU算法算法导入一、虚拟存储器的基本工作情况二、页面置换算法算法分析算法设计一、实现栈:二、实现主类:三、盲目分析四、敲代码算法源码及运行结果算法总结 算法导入        哈喽小伙伴们,英俊的我又回来了哈哈哈。今天给大家带来的是操作系统中的页面置换算法LRU算法实现。    &nb...

我家有只小金毛的博客 5882

医学图像分割评价指标详解:从Dice到MSD的实战解析

本文深入解析医学图像分割的核心评价指标,从最常用的Dice相似系数到衡量边界精度的MSD(最大对称表面距离),通过实战案例与代码演示,系统阐述各指标的计算原理、应用场景及临床意义,帮助读者全面评估模型性能。

weixin_29070721的博客 593

指标异常检测和诊断

贡献度,即用定量衡量的方式来说明:一个指标YYY的异动△Y△Y△Y,具体是由其中哪些成分XiX_iXi​带来了多少异动△Yi△Y_i△Yi​?

qq_43349542的博客 3163

LRU算法 java实现

定义:LRU全称Least Recently Used,也就是最近最少使用的意思,是一种内存管理算法,最早应用于Linux系统。 LRU算法基于一种假设:长期不使用的数据,在未来被用到的几率也不大。因此当数据所占内存达到一个阈值是,可以选择移除掉最近最少被使用的数据来保持内存的高效使用,这是基于时间局部性原理的一个假设。LRU算法主要用于缓存算法,在节省资源的情况下提高数据访问效率。 LRU使...

leo_weile的博客 7968

Python爬虫获取Shopee店铺的所有商品?

通过上述代码示例,我们可以轻松实现获取Shopee店铺所有商品信息的功能。Python爬虫不仅具有强大的功能和稳定性,还可以通过丰富的库支持实现高效的数据抓取和解析。希望这篇文章能为你提供一些启发和帮助。如果你对爬虫开发有更多兴趣,可以尝试探索更复杂的功能,如多线程爬取、数据可视化等。如果你在实践中遇到任何问题,欢迎随时交流和讨论。让我们一起用技术的力量,解锁更多可能!

2401_87195067的博客 1543

Java实现LRU算法

import java.util.HashMap; public class LRUCache { private Node head; private Node end; private int limit;//表示存储上限 private HashMap<String,Node> hashMap; public LRUCache(...

lc13571525583的博客 2192

基于Qt的工业组态软件开发:图形绘制与TCP通讯实现

在数据接收时,通过 readyRead 信号触发解析函数,校验数据包完整性后提取有效数据,并更新对应图形元素的状态,例如根据温度值改变矩形的填充色,实现设备状态可视化。测试阶段,我们将软件部署到Windows工控机,连接PLC设备进行通讯测试,结果表明图形元素响应及时,TCP数据传输稳定,满足工业现场的基本需求。综上所述,基于Qt的组态软件开发,核心在于利用 QGraphicsView 框架实现图形元素的灵活绘制与管理,结合 QTcpSocket 完成工业数据的可靠交互。

谢谢大家的关注和点赞!这里只有纯纯的知识干货,没有一句废话。希望能实实在在帮到大家~要是觉得有用,别忘了给我点支持哟,你 272

最新Anaconda3的安装配置及使用教程(详细过程)

最新Anaconda3安装使用Anaconda下载方式一:官网下载方式二:清华镜像下载(推荐)Anaconda安装 Anaconda是一个开源的Python发行版本,其包含了conda、Python等180多个科学包及其依赖项 Anaconda + Jupyter 基本上已经是大部分机器学习/数据分析等开发者标配的开发环境, 不多介绍,直接进去正题: Anaconda下载 方式一:官网下载 下载地址传送门: 官网首页:https://www.anaconda.com/ 官网下载页:https://www

HowieXue 薛永浩的博客 34万+

LRU算法Java实现

LRU(The Least Recently Used)即最近最少使用算法。其原理是如果一个数据最近没有被使用,那么将来它被使用的可能也更低,因此在内存达到一定阈值时,将最近最少使用的数据淘汰的一种策略算法。

MagicianTech的博客 2093

8. Spring Ai之入门到精通(超级详细)

Spring AI 1.0.0 M1里程碑版本发布,标志着 Java 终于踏进Ai的时代半只脚。

gorgor在码农 3万+

LRU算法的Java实现

LRU原理 LRU(Least recently used,最近最少使用)算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。 实现方式 要实现LRU算法,有 2 种实现方式。 第一种,是使用Java中现成的API —— LinkedHashMap。它在HashMap的基础增加了按照访问顺序排序的功能,非常适合LRU的实现。 代码如下: /...

makersy的博客 536

与本地电脑PDF文档对话的PDF问答程序

紧接着,从PDF文档中提取文本,这部分代码的作用是遍历PDF文档中的每一页,并使用extract text(0方法提取每一页的文本内容,然后将这些文本内容累加到raw text变量中。PDF问答程序的实现方式是利用LangChain已实现的向量存储、嵌入,以及使用查询和检索相关的链来获取外部数据集及处理文档,在进行相关性检索后进行合并处理,将其置入大语言模型的提示模板中,实现与PDF文件交流的目的。例如,你正在为一项重要的报告寻找资料,你必须知道你需要查找的关键词,然后在大量的信息中筛选出你需要的部分。

weixin_43961909的博客 1420

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

java 实现lru算法_LRU算法,LRU算法怎么实现?

下面要给大家介绍的就是和LRU算法有关的内容,你知道LRU算法应该怎样实现吗?实现的方式有哪些呢?一起来了解一下吧。LRU算法实现哈希链表结构:packageblogSrc;importjava.util.HashMap;publicclassLRUCache{privateNodehead;privateNodeend;privateHashMaphashMap;priva...

weixin_35828992的博客 137

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

java lru算法栈_LRU算法的Java实现

一、LRU算法介绍LRU算法全称Least Recently Used,也就是检查最近最少使用的数据的算法。这个算法通常使用在内存淘汰策略中,用于将不常用的数据转移出内存,将空间腾给最近更常用的“热点数据”。算法很简单,只需要将所有数据按使用时间排序,在需要筛选出LRU数据时,取排名靠后的即可。二、Java中的LRU实现思路根据LRU算法,在Java中实现需要这些条件:底层数据使用双向链表,方便在...

weixin_39999781的博客 319

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Java 实现 LRU 算法详解

LRU 算法的核心思想是,如果一个数据在最近一段时间没有被访问到,那么在将来它被访问的可能性也很小。当缓存空间已满时,最久没有被访问的数据将被淘汰,为新的数据腾出空间。通过使用 Java 实现 LRU 算法,我们可以有效地管理缓存,提高程序的性能。在实际应用中,LRU 算法可以用于数据库缓存、Web 服务器缓存等场景。希望本文对你理解和实现 LRU 算法有所帮助。

dawn191228的博客 1265
上一篇: 采用自动种子的方式搭建sqlserver alwaysOn,和备份恢复模块
下一篇: String类型可以被继承吗,为什么
code_feien
博客等级 码龄7年 71粉丝 85原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值