厚积薄发打卡Day97:哈希表(二)< 实现LRU缓存 >

题目二:

请设计实现一个最近最少使用( Least Recently Used,LRU)缓存,要求如下两个操作的时间复杂度都是O(1):

  • get(key):如果缓存中存在键key,则返回它对应的值;否则返回-1

  • put( key, value):如果缓存中之前包含键key,则它的值设为 value;否则添加键key及对应的值 value。

    在添加一个键时,如果缓存容量已经满了,则在添加新键之前删除最近最少使用的键(缓存中最长时间没有被使用过的元素)。

思路:

先来翻译翻译题目的意思:假设这里有个容量为3的缓存,依次存入key为A、B、C的元素:

在这里插入图片描述

得到缓存:
C --> B --> A

新增key为D的缓存
D --> C --> B

新增key为C的缓存
C --> D --> B

题目要求时间复杂度为O(1)的数据结构,那必须又要用到HashMap了,但有个问题就是HashMap无法记录键值的顺序,也就无法判断key的先后顺序了。

那有没有可以排序的map呢?有的:LinkedHashMap

LinkedHashMapHashMap的区别与联系:

  • LinkedHashMap是继承于HashMap,是基于HashMap和双向链表来实现的。
  • HashMap无序;LinkedHashMap有序,可分为插入顺序和访问顺序两种。如果是访问顺序,那put和get操作已存在的Entry时,都会把Entry移动到双向链表的表尾(其实是先删除再插入)。
  • LinkedHashMap存取数据,还是跟HashMap一样使用的Entry[]的方式,双向链表只是为了保证顺序。
  • LinkedHashMap是线程不安全的。

有了可以实现顺序的Map映射,那对于缓存的操作便迎刃而解了:

  • 存数据:
    • 当映射中存在key时,需先根据key移除,再放入K-V (此时存入的顺序为末位)
    • 当映射中不存在时
      • 如果小于容量:直接在映射后存入即可
      • 大于容量:则把映射首位数据移除再做存入操作
  • 取数据:
    • 直接根据key获取value即可

实现:

为了方便遍历实现了Iterable接口流式输出

public class LRUCache implements Iterable<String> {

    //缓存器容量
    int cap;

    public LRUCache(int cap) {
        this.cap = cap;
    }

    LinkedHashMap<String, Integer> cache = new LinkedHashMap<>();

    public int get(String key) {
        return cache.getOrDefault(key, -1);
    }

    public void put(String key, Integer value) {
        if (cache.containsKey(key)) {
            //如果存在当前key则需移除后才能添加
            cache.remove(key);
        } else if (cache.size() >= cap) {
            Iterator<String> it = cache.keySet().iterator();
            //容器满了,则需要将首个移除才能装下后续缓存
            String first = it.next();
            cache.remove(first);
        }
        cache.put(key, value);
    }


    @Override
    public Iterator<String> iterator() {

        // 等价
        return cache.keySet().iterator();
//        var it = cache.entrySet().iterator();
//        return new Iterator<>() {
//            @Override
//            public boolean hasNext() {
//                return it.hasNext();
//            }
//
//            @Override
//            public K next() {
//                return it.next().getKey();
//            }
//        };
    }
}

验证:

public static void main(java.lang.String[] argv) {
    LRUCache lru = new LRUCache(4);
    lru.put("A", 1);
    lru.put("B", 2);
    lru.put("C", 3);
    lru.put("D", 4);
    System.out.println(
            "leave <-" + StreamSupport.stream(lru.spliterator(), false).map(Object::toString)
                    .collect(Collectors.joining("<-")));
    System.out.println("C--" + lru.get("C"));
    lru.put("E", 5);
    lru.put("F", 4);
    lru.put("C", 10);
    System.out.println(
            "leave <-" + StreamSupport.stream(lru.spliterator(), false).map(Object::toString)
                    .collect(Collectors.joining("<-")));
}

输出:

leave <-A<-B<-C<-D
C--3
leave <-D<-E<-F<-C

验证成功!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值