首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

在Java中实现LRU Cache

,LRU(Least Recently Used,最近最少使用)是一种缓存淘汰算法,它根据数据的访问顺序来决定是否淘汰某个缓存项。以下是实现LRU Cache的步骤:

  1. 创建一个双向链表来维护缓存项的顺序,每个节点包含key和value两个属性。
  2. 创建一个HashMap来存储key和对应的节点对象,以实现快速查找。
  3. 设定缓存的容量大小,如果插入新的缓存项时超过容量大小,则需要删除最久未使用的缓存项。
  4. 在获取缓存项时,如果该项存在于缓存中,则将其移到链表的头部,表示最近使用过。
  5. 在插入新的缓存项时,如果该项已存在于缓存中,则更新其值,并将其移到链表头部。如果不存在于缓存中,则创建新的节点,并将其插入链表头部,同时将其添加到HashMap中。
  6. 在删除缓存项时,如果该项存在于缓存中,则将其从链表和HashMap中删除。

下面是一个示例代码实现:

代码语言:txt
复制
import java.util.HashMap;

class LRUCache {
    private int capacity;
    private Node head;
    private Node tail;
    private HashMap<Integer, Node> map;

    class Node {
        int key;
        int value;
        Node prev;
        Node next;

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

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

    public int get(int key) {
        if (map.containsKey(key)) {
            Node node = map.get(key);
            remove(node);
            addToHead(node);
            return node.value;
        }
        return -1;
    }

    public void put(int key, int value) {
        if (map.containsKey(key)) {
            Node node = map.get(key);
            node.value = value;
            remove(node);
            addToHead(node);
        } else {
            if (map.size() >= capacity) {
                map.remove(tail.prev.key);
                remove(tail.prev);
            }
            Node node = new Node(key, value);
            map.put(key, node);
            addToHead(node);
        }
    }

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

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

上述代码使用双向链表和HashMap实现了LRU Cache。双向链表用于维护缓存项的顺序,HashMap用于实现快速查找。LRUCache类的构造函数初始化了缓存的容量、双向链表的头部和尾部节点,以及HashMap。get()方法根据缓存项的键获取对应的节点,并将该节点移到链表的头部,表示最近使用过。put()方法插入新的缓存项时,如果容量已满,则删除链表尾部的节点和对应的HashMap中的键值对,然后将新的节点插入链表头部,并加入HashMap中。如果缓存项已存在,则更新其值并将其移到链表头部。

这是一个简单的LRU Cache实现示例,适用于小规模的缓存场景。在实际应用中,还需要根据具体需求对LRU Cache进行性能优化和线程安全处理。

关于腾讯云相关产品和产品介绍链接地址,可以参考腾讯云官方文档:https://cloud.tencent.com/document/product/583

页面内容是否对你有帮助?
有帮助
没帮助

相关·内容

面试官:本地缓存怎么选型?问倒一大片!

图片(2)ConcurrentHashMap 优化 Caffeine 底层都是通过 ConcurrentHashMap 来进行数据的存储,因此随着 Java8 中对 ConcurrentHashMap 的调整,数组 + 链表的结构升级为数组 + 链表 + 红黑树的结构以及分段锁升级为 syschronized+CAS,降低了锁的粒度,减少了锁的竞争,这两个优化显著提高了 Caffeine 在读多写少场景下的查询性能。 (3)新型淘汰算法 W-TinyLFU 传统的淘汰算法,如 LRU、LFU、FIFO,在实际的缓存场景中都存在一些弊端,如 FIFO 算法,如果缓存使用的频率较高,那么缓存数据会一直处在进进出出的状态,间接影响到缓存命中率。LRU 算法,在批量刷新缓存数据的场景下,可能会将其他缓存数据淘汰掉,从而带来缓存击穿的风险。LFU 算法,需要保存缓存记录的访问次数,带来内存空间的损耗。 因此,Caffeine 引入了 W-TinyLFU 算法,由窗口缓存、过滤器、主缓存组成。缓存数据刚进入时会停留在窗口缓存中,这个部分只占总缓存的 1%,当被挤出窗口缓存时,会在过滤器汇总和主缓存中淘汰的数据进行比较,如果频率更高,则进入主缓存,否则就被淘汰,主缓存被分为淘汰段和保护段,两段都是 LRU 算法,第一次被访问的元素会进入淘汰段,第二次被访问会进入保护段,保护段中被淘汰的元素会进入淘汰段,这种算法实现了高命中率和低内存占用。

01

【Redis】redis的过期策略能介绍一下?要不你再手写一个LRU?

1)noeviction:当内存不足以容纳新写入数据时,新写入操作会报错,这个一般没人用吧,实在是太恶心了 2)allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key(这个是最常用的) 3)allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key,这个一般没人用吧,为啥要随机,肯定是把最近最少使用的key给干掉啊 4)volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key(这个一般不太合适) 5)volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key 6)volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除

01

Ehcache食用指南

最近我们有个服务的时延(Latency)略微上涨,gc时间上涨了一倍,dump出java堆(Heap)之后用Mat分析发现,有份cache数据占据了20%+的堆内存,拥有上千万个小对象。然而这部分数据只是部分逻辑会用到,所以它占据这么大的堆内空间显得有些不值,并且会影响到gc进而影响到服务的时延。    当然也有一些其他数据也占用比较多的堆内空间,但做优化总是先拿大头开刀。 当然把这份数据去掉是不可能了,因为上面还承载着几个比较重要的业务逻辑。既然数据放到java 堆内影响gc,是否可以放到堆外?答案是肯定的,这也是我写这篇博客的目的。就是用Ehcahe把数据移动到堆外,ehcahe甚至可以把数据放到磁盘、放到远端服务器。

02
领券