Java基于LinkedHashMap实现LRU缓存

 更新时间:2025年06月03日 10:56:42   作者:喵手  
在很多实际的应用中,尤其是需要缓存数据的场景下,我们经常会遇到 LRU缓存,LRU 缓存是通过淘汰最久未使用的缓存数据来节省内存空间,在Java中,基于 LinkedHashMap实现LRU缓存是非常简便和高效的,所以本文介绍了如何基于LinkedHashMap实现LRU缓存,需要的朋友可以参考下

前言

在很多实际的应用中,尤其是需要缓存数据的场景下,我们经常会遇到 LRU(Least Recently Used,最近最少使用)缓存。LRU 缓存是通过淘汰最久未使用的缓存数据来节省内存空间。对于高效的 LRU 缓存,我们不仅要保证快速的查找、插入和删除操作,还要能够快速地淘汰最久未使用的元素。

在 Java 中,基于 LinkedHashMap 实现 LRU 缓存是非常简便和高效的,因为 LinkedHashMap 本身提供了按照访问顺序迭代的能力,我们可以利用这一特性轻松实现 LRU 缓存。

1. LinkedHashMap 简介

LinkedHashMap 是 HashMap 的一个子类,它基于哈希表实现,并且维护了插入顺序或访问顺序。这使得 LinkedHashMap 特别适合于实现缓存,尤其是在需要按访问顺序迭代时。

1.1 LinkedHashMap 的构造方法

  • LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder):
    • initialCapacity:初始容量。
    • loadFactor:负载因子。
    • accessOrder:如果设置为 true,则按照访问顺序排序;如果设置为 false,则按照插入顺序排序。

当 accessOrder 设置为 true 时,LinkedHashMap 会在每次访问(get 或 put 操作)时,将访问的元素移动到链表的末尾。这个特性让我们能够轻松地实现 LRU 缓存。

2. 基于 LinkedHashMap 实现 LRU 缓存

2.1 设计思路

  1. 缓存大小限制:我们需要为缓存设定一个最大容量 capacity,当缓存容量超过该值时,我们就需要淘汰最久未使用的元素。
  2. LRU 淘汰规则:在每次插入或访问元素时,我们将该元素移动到链表的末尾,这样链表的头部始终保存着最久未使用的元素。当缓存容量超过限制时,我们可以直接删除链表头部的元素。
  3. 使用 LinkedHashMap:利用 LinkedHashMap 中 accessOrder 的特性,结合 removeEldestEntry() 方法来自动删除最久未使用的元素。

2.2 实现步骤

我们可以创建一个继承自 LinkedHashMap 的类,并重写 removeEldestEntry() 方法,该方法会在每次插入新元素时被调用。

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

public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private final int capacity;

    // 构造函数,初始化容量和 accessOrder
    public LRUCache(int capacity) {
        super(capacity, 0.75f, true);  // 第三个参数 true 表示按访问顺序排序
        this.capacity = capacity;
    }

    // 重写 removeEldestEntry 方法,当缓存容量超出时,移除最久未使用的条目
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > capacity;
    }

    // 获取缓存中的值
    public V get(K key) {
        return super.getOrDefault(key, null);
    }

    // 插入缓存值
    public void put(K key, V value) {
        super.put(key, value);
    }
}

2.3 代码说明

  • LRUCache 类继承自 LinkedHashMap,并通过构造函数设置了 accessOrder 为 true,这使得每次访问元素时,该元素都会被移到链表的末尾。
  • removeEldestEntry() 方法会在每次插入新元素时检查缓存的大小。如果缓存的大小超过了设定的容量,它会返回 true,从而自动移除最久未使用的元素。
  • get(K key) 和 put(K key, V value) 方法分别用于获取和插入缓存中的数据。

2.4 测试案例

public class Main {
    public static void main(String[] args) {
        // 创建一个容量为 3 的 LRU 缓存
        LRUCache<Integer, String> cache = new LRUCache<>(3);

        // 向缓存中插入数据
        cache.put(1, "A");
        cache.put(2, "B");
        cache.put(3, "C");

        // 打印缓存内容
        System.out.println(cache); // 输出: {1=A, 2=B, 3=C}

        // 访问元素 1
        cache.get(1); // 使元素 1 最近访问

        // 插入新的元素,此时缓存超过容量,元素 2 将被移除
        cache.put(4, "D");

        // 打印缓存内容
        System.out.println(cache); // 输出: {3=C, 1=A, 4=D}
    }
}

输出:

{1=A, 2=B, 3=C}
{3=C, 1=A, 4=D}

2.5 解释

  1. 初始时,缓存的容量为 3,元素 {1=A, 2=B, 3=C} 被 插入缓存。
  2. 当访问 get(1) 时,元素 1 被移动到链表的末尾。
  3. 当插入元素 4=D 时,由于缓存已经满了,元素 2(最久未访问)被自动删除,最终缓存内容为 {3=C, 1=A, 4=D}

3. LRU 缓存优化

3.1 removeEldestEntry() 方法的灵活性

通过 removeEldestEntry() 方法,我们可以根据不同的需求定制缓存的淘汰规则。例如,我们可以根据某些条件(如元素的大小、元素的过期时间等)来决定是否删除最久未使用的元素。

3.2 内存管理

虽然 LinkedHashMap 的 accessOrder 特性和 removeEldestEntry() 方法让我们能够很方便地实现 LRU 缓存,但也需要注意缓存大小和内存使用的平衡。特别是当缓存需要存储大量数据时,合理设置缓存容量和定期清理缓存非常重要。

4. 总结

  • 使用 LinkedHashMap 实现 LRU 缓存的方式简洁高效,特别适合需要按访问顺序管理缓存数据的场景。
  • 通过重写 removeEldestEntry() 方法,我们能够在缓存超出容量时自动移除最久未使用的元素。
  • 这种方法不仅具有较高的性能,还能避免重复的复杂操作,方便开发者实现高效的缓存管理。

LRU 缓存的实现,帮助我们在高效处理数据时保持内存的合理使用,避免内存溢出或缓存过期问题的出现。

到此这篇关于Java基于LinkedHashMap实现LRU缓存的文章就介绍到这了,更多相关Java LRU缓存内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java异常javax.net.ssl.SSLHandshakeException: SSL的解决方法

    Java异常javax.net.ssl.SSLHandshakeException: SSL的解决方法

    在Java开发过程中,SSL(Secure Sockets Layer)握手异常是一个常见的网络通信错误,特别是在使用HTTPS协议进行安全通信时,本文将详细分析javax.net.ssl.SSLHandshakeException: SSL这一异常的背景、可能的原因,并通过代码示例帮助您理解和解决这一问题
    2024-12-12
  • Eclipse操作SVN时中断锁定,文件的解锁方法

    Eclipse操作SVN时中断锁定,文件的解锁方法

    这篇文章主要介绍了Eclipse操作SVN时中断锁定,文件的解锁方法,需要的朋友可以参考下
    2014-08-08
  • 一文带你掌握springBoot如何做到优雅停机的

    一文带你掌握springBoot如何做到优雅停机的

    在分布式系统中,服务的优雅停机(Graceful Shutdown)是确保业务连续性的重要机制,下面就跟随小编一起来深入了解下springBoot实现优雅停机的具体方式吧
    2025-04-04
  • spring在service层的方法报错事务不会回滚的解决

    spring在service层的方法报错事务不会回滚的解决

    这篇文章主要介绍了spring在service层的方法报错事务不会回滚的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-02-02
  • Java中Switch Case多个条件处理方法举例

    Java中Switch Case多个条件处理方法举例

    Java中switch语句用于根据变量值执行不同代码块,适用于多个条件的处理,这篇文章主要介绍了Java中Switch Case多个条件处理的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2025-04-04
  • SSM Mapper文件查询出返回数据查不到个别字段的问题

    SSM Mapper文件查询出返回数据查不到个别字段的问题

    这篇文章主要介绍了SSM Mapper文件查询出返回数据查不到个别字段的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • AndroidHttpClient使用Cookie应用分析

    AndroidHttpClient使用Cookie应用分析

    今天想把一个用使用了HttpClient的自动签到小程序移植到Android上,还好Android的SDK自带了HttpClient的包.当然也可以继续使用DefaultHttpClient,但用为Android定制的AndroidHttpClient自然更好
    2012-11-11
  • SpringBoot+RabbitMQ+Redis实现商品秒杀的示例代码

    SpringBoot+RabbitMQ+Redis实现商品秒杀的示例代码

    本文主要介绍了SpringBoot+RabbitMQ+Redis实现商品秒杀,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • java 使用Graphics2D在图片上写字

    java 使用Graphics2D在图片上写字

    这篇文章主要介绍了java 使用Graphics2D在图片上写字,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • 使用Mybatis-Plus实现对象属性自动填充功能

    使用Mybatis-Plus实现对象属性自动填充功能

    这篇文章主要介绍了如何使用Mybatis-Plus实现对象属性自动填充功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,感兴趣的朋友们下面随着小编来一起来学习吧
    2024-01-01

最新评论