《算法通关村——缓存机制了解LRU实现》

《算法通关村——缓存机制了解LRU实现》

介绍

LRU是"Least Recently Used"(最近最少使用)的缓存机制,它是一种常用的缓存算法,用于管理缓存中的数据项。LRU缓存机制的基本思想是,当缓存达到其容量限制时,会淘汰最久没有被访问的数据项,以为新的数据项腾出空间。

LRU缓存机制的实现方式通常是通过维护一个数据结构,例如链表或双向链表,来记录数据项的访问顺序。最近访问的数据项会被移到链表的头部(或双向链表的头部),而最久未被访问的数据项则位于链表的尾部。当需要从缓存中淘汰数据项时,就选择尾部的数据项进行淘汰,以确保淘汰最久未被访问的数据。

LRU缓存机制在实际应用中表现出良好的性能,因为它通常能够保留最常被访问的数据项,从而提高缓存命中率。然而,LRU的实现可能需要一定的额外开销,因为需要维护访问顺序的数据结构,而且在某些情况下,可能需要频繁地移动数据项,这可能会导致性能问题。因此,在特定情况下,可以考虑其他缓存淘汰算法,如LFU(Least Frequently Used)或FIFO(First-In, First-Out)等。

当涉及到缓存淘汰算法时,除了LRU,还有其他一些常见的算法,其中包括LFU(Least Frequently Used)和FIFO(First-In, First-Out):

  1. LFU(Least Frequently Used):

    • LFU算法基于数据项被访问的频率来进行缓存淘汰决策。它认为访问频率较低的数据项应该被淘汰,因为它们被认为是不太重要或不太常用的数据。
    • LFU算法通常使用一个计数器来跟踪每个数据项被访问的次数。当需要淘汰数据项时,选择具有最低访问次数的数据项进行淘汰。
    • 一些变种的LFU算法也考虑了时间因素,以便更好地适应数据访问模式的变化。这些变种可能会在计数器的基础上引入时间衰减因素。
  2. FIFO(First-In, First-Out):

    • FIFO算法是一种非常简单的缓存淘汰策略。它按照数据项最早进入缓存的顺序来进行淘汰。也就是说,首先进入缓存的数据项最先被淘汰,而最后进入的数据项最后被淘汰。
    • FIFO算法通常使用一个队列来维护数据项的顺序。当需要淘汰数据项时,从队列的头部移除最早进入的数据项。
    • FIFO算法的主要优点是其简单性,但它可能不太适用于某些访问模式,因为它不考虑数据项的访问频率或重要性,只根据进入顺序进行淘汰。

选择何种缓存淘汰算法通常取决于具体的应用场景和需求。不同的算法有不同的优缺点,因此需要根据数据访问模式、性能需求以及实际应用的特点来选择最合适的算法。在某些情况下,也可以考虑结合多个算法或自定义淘汰策略以满足特定的需求。

代码实现

package AlgorithmFifth;

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

public class LRUCache {
    class DLinkedNode {
        int key;
        int value;
        DLinkedNode prev;
        DLinkedNode next;

        public DLinkedNode() {
        }

        public DLinkedNode(int _key,int _value){
            key = _key;
            value = _value;
        }
    }
    private Map<Integer,DLinkedNode> cache = new HashMap<>();
    private int size;
    private int capacity;
    private DLinkedNode head,tail;

    public LRUCache(int capacity){
        this.size = 0;
        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;
        }
        // 如果key存在,先通过哈希表定位,再移到头部
        moveToHead(node);
        return node.value;
    }

    public void put(int key,int value){
        DLinkedNode node = cache.get(key);
        if(node == null){
            // 如果key不存在创建一个新节点
            DLinkedNode newNode = new DLinkedNode(key,value);
            // 添加进哈希表
            cache.put(key,newNode);
            // 添加至双向链表的头部
            addToHead(newNode);
            ++size;
            if(size > capacity){
                // 如果超出容量,删除双向链表的尾部节点
                DLinkedNode tail = removeTail();
                // 删除哈希表中对应的项
                cache.remove(tail.key);
                size--;
            }
        }else {
            // 如果key存在,先通过哈希表定位,在修改value,并移到头部
            node.value = value;
            moveToHead(node);
        }
    }
    private void addToHead(DLinkedNode node) {
        node.prev = head;
        node.next = head.next;
        head.next.prev = node;
        head.next = node;
    }

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

    private void moveToHead(DLinkedNode node){
        removeNode(node);
        addToHead(node);
    }

    private DLinkedNode removeTail(){
        DLinkedNode res = tail.prev;
        removeNode(res);
        return res;
    }

    public static void main(String[] args) {
        LRUCache lruCache = new LRUCache(2);
        lruCache.put(1,1);
        lruCache.put(2,2);
        System.out.println(lruCache.get(1));
        lruCache.put(3,3);
        System.out.println(lruCache.get(2));
        lruCache.put(4,4);
        System.out.println(lruCache.get(1));
        System.out.println(lruCache.get(3));
        System.out.println(lruCache.get(4));
    }
}

近期在自学 Java 做项目,加入了一个编程学习圈子,里面有编程学习路线和原创的项目教程,感觉非常不错。还可以 1 对 1 和大厂嘉宾交流答疑,也希望能对大家有帮助,扫 ⬇️ 二维码即可加入。

在这里插入图片描述

也可以点击链接:我正在「编程导航」和朋友们讨论有趣的话题,你⼀起来吧?

也可以加我QQ(2837468248)咨询说明来意!

你可能感兴趣的:(算法学习,算法,缓存,spring)