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则提供了更高的灵活性。开发者可以根据实际需求选择合适的实现方式。


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

参考文章

相关推荐
暴怒香菜统治世界2 分钟前
c语言--结构体
c语言·开发语言·数据结构·笔记·算法
WHabcwu9 分钟前
Spring Web MVC⼊⻔
java·后端·spring·mvc
coffee_baby11 分钟前
《解锁高效流程设计:深度剖析责任链模式与实战应用》
java·开发语言·责任链模式
予早12 分钟前
LeetCode 427. 建立四叉树
算法·leetcode
customer0814 分钟前
【开源免费】基于SpringBoot+Vue.JS服装销售平台(JAVA毕业设计)
java·vue.js·spring boot·后端·spring cloud·开源·intellij-idea
流星白龙16 分钟前
【C++算法】4.双指针_快乐数
c++·算法
晴子呀17 分钟前
一个有趣的编程题实战----交替打印线程
java·开发语言
羑悻的小杀马特20 分钟前
AVL树(平衡二叉树)的介绍以及相关构建
c++·算法·平衡二叉树·avl树
叶辰 .35 分钟前
POI获取模板文件,替换数据横纵动态表格、折线图、饼状图、折线饼状组合图
java
胡净1 小时前
java并发线程02
java·开发语言