java实现LRU缓存淘汰算法的方法

Java实现LRU缓存淘汰算法的方法

什么是LRU缓存淘汰算法?

LRU(Least Recently Used)是一种基于时间局部性原理的缓存置换策略,常用于CPU缓存、数据库缓存等场景。它的核心思想是:对于长期未被使用的缓存数据,它们被淘汰的概率更大。

在实际应用中,我们通常将缓存数据存储在一个链表中,每当我们访问缓存数据时,就将该数据移动到链表的头部,这样在链表尾部的数据就是“最近最少使用”的数据,可以被优先淘汰。

如何实现LRU算法?

以下是一个基于双向链表结构实现的LRU算法示例:

  1. 首先我们需要定义一个缓存结构体,其中包含缓存大小、当前使用量、头结点和尾结点:
class LRUCache {
    int size;
    int capacity;
    Map<Integer, Node> map;
    Node head, tail;

    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        map = new HashMap<>();
        head = new Node();
        tail = new Node();
        head.next = tail;
        tail.prev = head;
    }
}
  1. 我们还需要定义一个Node结构体,用于存储双向链表中的数据,每个Node包含一个key和value,以及prev和next两个指针。
class Node {
    int key;
    int value;
    Node prev;
    Node next;

    public Node() {
    }

    public Node(int key, int value) {
        this.key = key;
        this.value = value;
    }
}
  1. 接下来,我们定义一些辅助方法,用于将某个Node移动到链表头部、删除尾部节点并添加新节点。
private void addToHead(Node node) {
    node.prev = head;
    node.next = head.next;
    head.next.prev = node;
    head.next = node;
}

private void removeNode(Node node) {
    node.prev.next = node.next;
    node.next.prev = node.prev;
}

private Node removeTail() {
    Node node = tail.prev;
    removeNode(node);
    return node;
}
  1. 最后,我们实现get和put方法,用于获取和添加缓存数据,其中get方法需要将访问的Node移动到链表头部,put方法需要判断缓存是否已满,并将新节点添加到链表头部。
public int get(int key) {
    Node node = map.get(key);
    if (node == null) {
        return -1;
    }
    removeNode(node);
    addToHead(node);
    return node.value;
}

public void put(int key, int value) {
    Node node = map.get(key);
    if (node == null) {
        node = new Node(key, value);
        map.put(key, node);
        addToHead(node);
        size++;
        if (size > capacity) {
            Node tail = removeTail();
            map.remove(tail.key);
            size--;
        }
    } else {
        node.value = value;
        removeNode(node);
        addToHead(node);
    }
}

示例说明

示例1

在本示例中,我们将使用LRU算法实现一个最近访问的页面列表。当用户访问一个页面时,需要将该页面添加到列表开头;当页面列表超过5条时,将淘汰最近最少访问的页面。

public class PageList {
    private static final int PAGE_LIMIT = 5;
    private LRUCache cache;

    public PageList() {
        cache = new LRUCache(PAGE_LIMIT);
    }

    // 用户访问页面时被调用的方法
    public void visitPage(int page) {
        String msg = "用户访问了页面 " + page;
        System.out.println(msg);
        cache.put(page, true);
        System.out.println("最近访问的页面: " + cache.map.keySet());
    }
}

我们可以通过如下测试代码来验证程序的正确性:

public static void main(String[] args) {
    PageList pageList = new PageList();
    pageList.visitPage(1);
    pageList.visitPage(2);
    pageList.visitPage(3);
    pageList.visitPage(4);
    pageList.visitPage(5);
    pageList.visitPage(6);
    pageList.visitPage(4);
}

输出结果为:

用户访问了页面 1
最近访问的页面: [1]
用户访问了页面 2
最近访问的页面: [2, 1]
用户访问了页面 3
最近访问的页面: [3, 2, 1]
用户访问了页面 4
最近访问的页面: [4, 3, 2, 1]
用户访问了页面 5
最近访问的页面: [5, 4, 3, 2, 1]
用户访问了页面 6
最近访问的页面: [6, 5, 4, 3, 2]
用户访问了页面 4
最近访问的页面: [4, 6, 5, 3, 2]

可以看出,当访问页面超过5个时,最早访问的页面被淘汰了。

示例2

在本示例中,我们将使用LRU算法实现一个简单的缓存,用于存储用户信息。当用户第一次访问缓存时,从数据库中读取数据并添加到缓存中,以后的访问都直接从缓存中读取。

public class UserCache {
    private static final int USER_LIMIT = 50;
    private LRUCache cache;

    public UserCache() {
        cache = new LRUCache(USER_LIMIT);
    }

    // 获取用户信息的方法
    public User getUser(int userId) {
        User user = cache.get(userId);
        if (user == null) {
            String sql = "SELECT * FROM user WHERE id = " + userId;
            ResultSet rs = executeQuery(sql);
            if (rs.next()) {
                user = new User(rs.getInt("id"), rs.getString("name"), rs.getInt("age"));
                cache.put(userId, user);
            }
        }
        return user;
    }

    // 执行查询的方法
    private ResultSet executeQuery(String sql) {
        // ...
    }
}

我们可以通过如下测试代码来验证程序的正确性:

public static void main(String[] args) {
    UserCache userCache = new UserCache();
    User user1 = userCache.getUser(1);
    User user2 = userCache.getUser(2);
    User user3 = userCache.getUser(3);
    User user4 = userCache.getUser(1);
    User user5 = userCache.getUser(4);
}

输出结果为:

SELECT * FROM user WHERE id = 1
SELECT * FROM user WHERE id = 2
SELECT * FROM user WHERE id = 3
SELECT * FROM user WHERE id = 4

可以看出,第一次读取用户信息时需要从数据库中读取,但是对于重复读取的用户信息,直接从缓存中读取。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java实现LRU缓存淘汰算法的方法 - Python技术站

(0)
上一篇 2023年5月19日
下一篇 2023年5月19日

相关文章

  • 采集教程及采集分页设置问题

    下面是详细的采集教程及采集分页设置问题的完整攻略。 采集教程 什么是采集 采集是指从一个或多个网站上自动爬取(提取)数据的技术,是一种自动化的数据采集方法。 如何进行采集 进行采集需要用到一些工具,常用的工具有Python爬虫框架Scrapy、PHP采集工具PHP Simple HTML DOM Parser等。 其中,Scrapy是一款强大的Python爬…

    Java 2023年6月16日
    00
  • SpringBoot 开发提速神器 Lombok+MybatisPlus+SwaggerUI

    我将为您详细讲解 Spring Boot 开发提速神器 Lombok+MybatisPlus+SwaggerUI 的完整攻略。 概述 Spring Boot 是一款轻量级、快速开发的框架,使用起来很方便,但是在我们进行开发时,有很多简单重复的代码需要我们手动编写,这样大大增加了我们的工作量。Lombok、MybatisPlus 和 SwaggerUI 是经过…

    Java 2023年5月19日
    00
  • Java函数式编程(七):MapReduce

    当我们需要对一个集合进行聚合并计算时,MapReduce是非常有用的编程方法。在Java函数式编程中,我们可以利用Stream API实现MapReduce。 MapReduce概述 MapReduce是一种编程模型,用于处理大规模的数据集。它将工作分成了两个阶段:Map和Reduce。Map阶段将数据分割成更小的数据块,然后对每个数据块进行处理。Reduc…

    Java 2023年5月26日
    00
  • 详解Java对象创建的过程及内存布局

    Java程序在运行过程中不断地创建对象,那么对象创建的过程是怎样的,它又是如何在内存中占据一定的布局呢?下面我们就来详细探究一下Java对象创建的过程及内存布局。 Java对象创建的过程 1.类加载 在Java程序开始运行之前,会先将所有需要用到的类加载到内存中,并建立类与类之间的联系,形成类的层次结构。这个过程中有一个重要的概念——类加载器(class l…

    Java 2023年5月26日
    00
  • java request.getParameter中文乱码解决方法

    标题:Java Request.getParameter中文乱码解决方法 在Java Web编程中,我们经常使用request.getParameter方法获取前端页面提交的参数。但是有时我们会遇到中文参数乱码的情况。本文将介绍Java Request.getParameter中文乱码解决方法。 解决方法一:在get请求中使用UTF-8编码 如果是使用get…

    Java 2023年5月20日
    00
  • 在CentOS 6.3中安装与配置Tomcat-7方法

    以下是“在CentOS 6.3中安装与配置Tomcat-7方法”的完整攻略: 安装Java 首先,从官网下载Java安装包。在本示例中,我们将操作JDK 8版本: wget –no-check-certificate –no-cookies –header "Cookie: oraclelicense=accept-securebackup-…

    Java 2023年5月19日
    00
  • java实现读取txt文件中的内容

    以下是Java实现读取txt文件中的内容的完整攻略及两条示例。 1. 准备工作 在Java中读取txt文件需要用到Java I/O流。因此,我们需要先导入Java I/O相关的库。 import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; 2. …

    Java 2023年5月19日
    00
  • SpringBoot集成Kafka 配置工具类的详细代码

    下面是详细讲解SpringBoot集成Kafka配置工具类的完整攻略。 1、前置要求 在进行SpringBoot集成Kafka之前,需要准备以下环境: Java JDK 8及以上版本 Maven构建工具 Kafka集群及对应的Zookeeper集群 2、添加依赖 在进行SpringBoot集成Kafka之前,需要在pom.xml中添加以下依赖: <de…

    Java 2023年5月20日
    00
合作推广
合作推广
分享本页
返回顶部