最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java LRU 與 LFU 算法及應(yīng)用小結(jié)

 更新時間:2026年07月06日 09:53:54   作者:Full Stack Developme  
LRU和LFU是兩種主要的緩存淘汰算法,LRU基于最近訪問時間淘汰數(shù)據(jù),LFU基于訪問頻率淘汰數(shù)據(jù),下面就來詳細(xì)的介紹一下Java LRU 與 LFU 算法及應(yīng)用小結(jié),感興趣的可以了解一下

在Java面試和日常開發(fā)中,LRULFU是出現(xiàn)頻率最高的兩種緩存淘汰算法。它們的核心區(qū)別在于淘汰依據(jù):LRU看“時間”(最近誰沒用過),LFU看“頻率”(誰最不常用)。

1. LRU(Least Recently Used,最近最少使用)

核心思想

淘汰最長時間未被訪問的數(shù)據(jù)。假設(shè)“如果數(shù)據(jù)最近被訪問過,那么將來被訪問的概率也更高”。

Java實(shí)現(xiàn)方式

方式一:LinkedHashMap一行搞定(最推薦)

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

    public LRUCache(int capacity) {
        // accessOrder=true 表示按訪問順序排序
        super(capacity, 0.75f, true);
        this.maxCapacity = capacity;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > maxCapacity;  // 容量超標(biāo)時移除最老元素
    }
}

使用示例

LRUCache<Integer, String> cache = new LRUCache<>(3);
cache.put(1, "A");
cache.put(2, "B");
cache.put(3, "C");
cache.get(1);        // 訪問1,使其變?yōu)樽钚?
cache.put(4, "D");   // 觸發(fā)淘汰,移除最久未用的2
System.out.println(cache); // {3=C, 1=A, 4=D}

方式二:手寫雙向鏈表 + HashMap(面試??迹?/h4>
class LRUCacheManual {
    class DLinkedNode {
        int key, value;
        DLinkedNode prev, next;
        DLinkedNode() {}
        DLinkedNode(int key, int value) { this.key = key; this.value = value; }
    }

    private Map<Integer, DLinkedNode> cache = new HashMap<>();
    private int size, capacity;
    private DLinkedNode head, tail;

    public LRUCacheManual(int capacity) {
        this.capacity = capacity;
        head = new DLinkedNode();
        tail = new DLinkedNode();
        head.next = tail;
        tail.prev = head;
    }

    public int get(int key) {
        DLinkedNode node = cache.get(key);
        if (node == null) return -1;
        moveToHead(node);  // 移到鏈表頭部(表示最近使用)
        return node.value;
    }

    public void put(int key, int value) {
        DLinkedNode node = cache.get(key);
        if (node != null) {
            node.value = value;
            moveToHead(node);
        } else {
            DLinkedNode newNode = new DLinkedNode(key, value);
            cache.put(key, newNode);
            addToHead(newNode);
            size++;
            if (size > capacity) {
                DLinkedNode tail = removeTail();
                cache.remove(tail.key);
                size--;
            }
        }
    }
    // 省略 addToHead, removeTail, moveToHead 等鏈表操作...
}

2. LFU(Least Frequently Used,最不經(jīng)常使用)

核心思想

淘汰訪問次數(shù)最少的數(shù)據(jù)。如果訪問次數(shù)相同,則淘汰最久未使用的(需要結(jié)合時間)。

實(shí)現(xiàn)難點(diǎn)

LFU比LRU復(fù)雜得多,因?yàn)樾枰瑫r維護(hù)頻率時間兩個維度。LeetCode 460題就是典型。

經(jīng)典實(shí)現(xiàn)(雙哈希表 + 頻率鏈表)

class LFUCache {
    private Map<Integer, Node> keyToNode;    // key -> 數(shù)據(jù)節(jié)點(diǎn)
    private Map<Integer, LinkedHashSet<Node>> freqToKeys; // 頻率 -> 該頻率下的key集合(用LinkedHashSet保證順序)
    private int minFreq;  // 當(dāng)前最小頻率
    private int capacity;

    class Node {
        int key, value, freq;
        Node(int key, int value) {
            this.key = key;
            this.value = value;
            this.freq = 1;  // 初始頻率為1
        }
    }

    public LFUCache(int capacity) {
        this.capacity = capacity;
        keyToNode = new HashMap<>();
        freqToKeys = new HashMap<>();
        minFreq = 0;
    }

    public int get(int key) {
        if (!keyToNode.containsKey(key)) return -1;
        Node node = keyToNode.get(key);
        updateFreq(node);  // 訪問后頻率+1
        return node.value;
    }

    public void put(int key, int value) {
        if (capacity == 0) return;
        if (keyToNode.containsKey(key)) {
            Node node = keyToNode.get(key);
            node.value = value;
            updateFreq(node);
        } else {
            if (keyToNode.size() >= capacity) {
                // 淘汰最小頻率中最久未使用的那個
                LinkedHashSet<Node> set = freqToKeys.get(minFreq);
                Node oldest = set.iterator().next();
                set.remove(oldest);
                keyToNode.remove(oldest.key);
                if (set.isEmpty()) freqToKeys.remove(minFreq);
            }
            Node newNode = new Node(key, value);
            keyToNode.put(key, newNode);
            freqToKeys.computeIfAbsent(1, k -> new LinkedHashSet<>()).add(newNode);
            minFreq = 1;  // 新插入的一定是頻率最低
        }
    }

    private void updateFreq(Node node) {
        int oldFreq = node.freq;
        // 從舊頻率集合中移除
        LinkedHashSet<Node> oldSet = freqToKeys.get(oldFreq);
        oldSet.remove(node);
        if (oldSet.isEmpty()) {
            freqToKeys.remove(oldFreq);
            if (minFreq == oldFreq) minFreq++;
        }
        // 放入新頻率集合
        node.freq++;
        freqToKeys.computeIfAbsent(node.freq, k -> new LinkedHashSet<>()).add(node);
    }
}

3. 應(yīng)用場景對比

維度LRULFU
適用場景熱點(diǎn)數(shù)據(jù)有明顯時效性(如用戶最近瀏覽)熱點(diǎn)數(shù)據(jù)穩(wěn)定且長期(如熱門視頻、全局配置)
典型例子Redis內(nèi)存淘汰(allkeys-lru)、Glide圖片緩存、瀏覽器后退按鈕數(shù)據(jù)庫查詢緩存(統(tǒng)計高頻SQL)、CDN內(nèi)容緩存
優(yōu)點(diǎn)實(shí)現(xiàn)簡單,性能高,符合局部性原理能過濾掉偶發(fā)性突發(fā)流量,保留真正高頻數(shù)據(jù)
缺點(diǎn)容易被批量訪問"污染"(一次全表掃描會把所有舊數(shù)據(jù)擠出去)實(shí)現(xiàn)復(fù)雜,維護(hù)成本高;新數(shù)據(jù)剛進(jìn)來頻率低,容易被立即淘汰(需加老化機(jī)制)

4. 生產(chǎn)環(huán)境中的最佳實(shí)踐

① 直接使用成熟緩存框架

不要自己寫LFU,99%的場景用Google的Caffeine就夠了:

// Caffeine 同時支持 LRU 和 LFU(實(shí)際是 TinyLFU,LFU的增強(qiáng)版)
Cache<String, Object> cache = Caffeine.newBuilder()
    .maximumSize(1000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .recordStats()
    .build();

Caffeine使用的TinyLFU(論文算法)結(jié)合了LFU和LRU的優(yōu)點(diǎn),并解決了新數(shù)據(jù)"饑餓"問題。

② Redis中的使用

  • LRU:Redis默認(rèn)近似LRU(非精確,采樣淘汰),配置maxmemory-policy allkeys-lru
  • LFU:Redis 4.0+支持,配置allkeys-lfu,適合穩(wěn)定的熱點(diǎn)數(shù)據(jù)緩存

③ 數(shù)據(jù)庫連接池或線程池

使用LinkedBlockingDeque實(shí)現(xiàn)簡單LRU,淘汰長時間空閑的連接。

5. 面試高頻追問

Q:為什么LinkedHashMap能實(shí)現(xiàn)LRU?
A:構(gòu)造參數(shù)accessOrder=true使鏈表按訪問順序排序,每次get/put都會將節(jié)點(diǎn)移到尾部,頭部就是最久未用的。重寫removeEldestEntry控制淘汰時機(jī)。

Q:LRU和LFU哪個更優(yōu)?
A:沒有絕對優(yōu)劣。突發(fā)流量場景(如雙11秒殺)用LRU更合適;穩(wěn)態(tài)熱點(diǎn)場景(如排行榜)用LFU更精準(zhǔn)。工業(yè)界常用兩者混合(如Arc算法、TinyLFU)。

Q:如何解決LFU中"新數(shù)據(jù)剛來就被淘汰"的問題?
A:給新數(shù)據(jù)初始頻率賦一個較高的值(如初始頻率=5),或使用窗口計數(shù)(只統(tǒng)計最近一段時間內(nèi)的訪問次數(shù)),Caffeine的TinyLFU就是這么做的。

到此這篇關(guān)于Java LRU 與 LFU 算法及應(yīng)用小結(jié)的文章就介紹到這了,更多相關(guān)Java LRU LFU內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • java字符串與格式化輸出的深入分析

    java字符串與格式化輸出的深入分析

    本篇文章是對java字符串與格式化輸出進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • 在mybatis中去除多余的前綴或者后綴操作

    在mybatis中去除多余的前綴或者后綴操作

    這篇文章主要介紹了在mybatis中去除多余的前綴或者后綴操作。具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-11-11
  • SpringMVC的概念以及快速入門示例

    SpringMVC的概念以及快速入門示例

    這篇文章主要介紹了SpringMVC的概念以及快速入門示例,SpringMVC 已經(jīng)成為目前最主流的MVC框架之一,它通過一套注解,讓一個簡單的 Java 類成為處理請求的控制器,而無須實(shí)現(xiàn)任何接口,需要的朋友可以參考下
    2023-05-05
  • SpringBoot創(chuàng)建maven多模塊項(xiàng)目實(shí)戰(zhàn)代碼

    SpringBoot創(chuàng)建maven多模塊項(xiàng)目實(shí)戰(zhàn)代碼

    本篇文章主要介紹了SpringBoot創(chuàng)建maven多模塊項(xiàng)目實(shí)戰(zhàn)代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • Springboot自定義mvc組件如何實(shí)現(xiàn)

    Springboot自定義mvc組件如何實(shí)現(xiàn)

    這篇文章主要介紹了Springboot自定義mvc組件如何實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-11-11
  • 一篇文章帶你了解Maven的繼承和聚合

    一篇文章帶你了解Maven的繼承和聚合

    這篇文章主要為大家介紹了Maven的繼承和聚合,具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助
    2022-01-01
  • Java調(diào)用第三方http接口的四種方式總結(jié)

    Java調(diào)用第三方http接口的四種方式總結(jié)

    這篇文章主要給大家介紹了關(guān)于Java調(diào)用第三方http接口的四種方式,在實(shí)際開發(fā)中我們經(jīng)常會與第三方公司進(jìn)行合作,接入第三方接口,文中給出了詳細(xì)的代碼實(shí)例,需要的朋友可以參考下
    2023-08-08
  • Java LinkedList集合功能實(shí)例解析

    Java LinkedList集合功能實(shí)例解析

    這篇文章主要介紹了Java LinkedList集合功能實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-04-04
  • 如何通過SpringBoot實(shí)現(xiàn)商城秒殺系統(tǒng)

    如何通過SpringBoot實(shí)現(xiàn)商城秒殺系統(tǒng)

    這篇文章主要介紹了如何通過SpringBoot實(shí)現(xiàn)商城秒殺系統(tǒng),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11
  • Springboot實(shí)現(xiàn)郵件發(fā)送功能

    Springboot實(shí)現(xiàn)郵件發(fā)送功能

    這篇文章主要為大家詳細(xì)介紹了Springboot實(shí)現(xiàn)郵件發(fā)送功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-02-02

最新評論

大关县| 寿阳县| 保山市| 教育| 梨树县| 萍乡市| 宁阳县| 新田县| 汕尾市| 华安县| 枣强县| 石渠县| 娄烦县| 儋州市| 无为县| 延边| 新邵县| 文化| 南丰县| 彭阳县| 阜南县| 分宜县| 伊春市| 河池市| 南涧| 龙泉市| 中山市| 乌恰县| 双辽市| 竹山县| 威信县| 黔江区| 财经| 旅游| 阜康市| 安宁市| 贵阳市| 惠来县| 扬州市| 广水市| 揭东县|