Java实现LRU(最近最少使用)算法

Java实现LRU(最近最少使用)算法

一、引言

LRU(Least Recently Used)算法,即最近最少使用算法,是一种常见的缓存淘汰策略。它基于局部性原理,即最近被访问的数据在未来一段时间内被访问的概率更高,而长时间未被访问的数据在未来一段时间内被访问的概率较低。在Java中实现LRU算法,可以有效地管理缓存数据,提高系统性能。

二、实现方式

在Java中实现LRU算法主要有两种方式:使用LinkedHashMap和自定义双向链表结合HashMap

1、使用LinkedHashMap

Java的LinkedHashMap类提供了一个按访问顺序排序的功能,可以用来实现LRU缓存。通过设置accessOrdertrueLinkedHashMap会将最近访问的元素移动到链表的末尾,而最老的元素(链表的头部)则是最少被访问的元素。

1.1、步骤
  1. 创建一个LinkedHashMap实例,并设置一个初始容量和加载因子。
  2. 通过putget方法操作缓存,LinkedHashMap会自动维护元素的访问顺序。
  3. 重写removeEldestEntry方法,当缓存容量超出限制时,自动移除最老的元素。

2、自定义双向链表结合HashMap

这种方式需要自己实现一个双向链表来维护缓存数据的顺序,同时使用HashMap来存储键和对应节点的引用,以实现快速查找。

2.1、步骤
  1. 定义一个双向链表节点类,包含键、值、前驱和后继指针。
  2. 创建一个缓存类,内部包含一个HashMap和一个双向链表。
  3. getput方法中,根据访问情况更新节点在链表中的位置。
  4. 当缓存容量超出限制时,移除链表尾部的节点,即最老的元素。

三、代码实现

1、使用LinkedHashMap

java 复制代码
/**
 * @author ning0
 * @date 2024/9/23 12:03
 * @description Main
 **/
import java.util.LinkedHashMap;
import java.util.Map;

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

    public LRUCache(int capacity) {
        super(capacity, 0.75f, true);
        this.capacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > capacity;
    }

    public static void main(String[] args) {
        LRUCache<Integer, String> cache = new LRUCache<>(3);
        cache.put(1, "one");
        cache.put(2, "two");
        cache.put(3, "three");
        cache.get(2); // 访问2,将其移到链表尾部
        cache.put(4, "four"); // 容量超出,移除最老的元素1
        System.out.println(cache.keySet()); // 输出:[3, 2, 4]
    }
}

2、自定义双向链表结合HashMap

java 复制代码
/**
 * @author ning0
 * @date 2024/9/23 12:03
 * @description Main
 **/

import java.util.HashMap;
import java.util.Map;

class LRUCache<K, V> {
    private final int capacity;
    private final Map<K, Node<K, V>> map;
    private final Node<K, V> head, tail;

    public LRUCache(int capacity) {
        this.capacity = capacity;
        map = new HashMap<>();
        head = new Node<>(null, null);
        tail = new Node<>(null, null);
        head.next = tail;
        tail.prev = head;
    }

    public V get(K key) {
        Node<K, V> node = map.get(key);
        if (node == null) return null;
        moveToHead(node);
        return node.value;
    }

    public void put(K key, V value) {
        Node<K, V> node = map.get(key);
        if (node == null) {
            node = new Node<>(key, value);
            map.put(key, node);
            addNode(node);
            if (map.size() > capacity) {
                map.remove(tail.prev.key);
                removeNode(tail.prev);
            }
        } else {
            node.value = value;
            moveToHead(node);
        }
    }

    private void addNode(Node<K, V> node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

    private void removeNode(Node<K, V> node) {
        node.prev.next = node.next;
        node.next.prev = node.prev;
    }

    private void moveToHead(Node<K, V> node) {
        removeNode(node);
        addNode(node);
    }

    class Node<K, V> {
        K key;
        V value;
        Node<K, V> prev;
        Node<K, V> next;

        Node(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder("[");
        Node<K, V> curr = head.next;
        while (curr != tail) {
            sb.append(curr.key).append(", ");
            curr = curr.next;
        }
        return sb.delete(sb.length() - 2, sb.length()).append("]").toString();
    }

    public static void main(String[] args) {
        LRUCache<Integer, String> cache = new LRUCache<>(3);
        cache.put(1, "one");
        cache.put(2, "two");
        cache.put(3, "three");
        cache.get(2); // 访问2,将其移到链表头部
        cache.put(4, "four"); // 容量超出,移除最老的元素1
        System.out.println(cache); // 输出:[4, 2, 3]
    }
}

四、总结

LRU算法在缓存管理中扮演着重要角色,通过合理地淘汰最不常访问的数据,可以提高缓存的利用率和系统的性能。Java中实现LRU算法的两种方式各有优势,LinkedHashMap实现简单快捷,而自定义双向链表结合HashMap则提供了更高的灵活性。开发者可以根据实际需求选择合适的实现方式。


版权声明:本博客内容为原创,转载请保留原文链接及作者信息。

参考文章

相关推荐
落魄君子11 分钟前
SVM分类-支持向量机(Support Vector Machine)
神经网络·算法·支持向量机·分类
zxguan13 分钟前
Springboot 学习 之 logback-spring.xml 日志压缩 .tmp 临时文件问题
spring boot·学习·spring
陈大爷(有低保)15 分钟前
logback日志控制台打印与写入文件
java
繁川15 分钟前
深入理解Spring AOP
java·后端·spring
Am心若依旧40916 分钟前
[c++进阶(三)]单例模式及特殊类的设计
java·c++·单例模式
上理考研周导师1 小时前
【单片机原理】第1章 微机基础知识,运算器,控制器,寄存器,微机工作过程,数制转换
算法
IT猿手2 小时前
基于PWLCM混沌映射的麋鹿群优化算法(Elk herd optimizer,EHO)的多无人机协同路径规划,MATLAB代码
算法·elk·机器学习·matlab·无人机·聚类·强化学习
ZHOUPUYU3 小时前
最新 neo4j 5.26版本下载安装配置步骤【附安装包】
java·后端·jdk·nosql·数据库开发·neo4j·图形数据库
Q_19284999064 小时前
基于Spring Boot的找律师系统
java·spring boot·后端
谢家小布柔5 小时前
Git图形界面以及idea中集合Git使用
java·git