LeetCode 高级 - LRU 缓存机制

LRU 缓存机制

运用你所掌握的数据结构,设计和实现一个 LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

进阶:

你是否可以在 O(1) 时间复杂度内完成这两种操作?

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

分析

首先,为了不愿意往下看的同学,直接给出核心点:

  1. 令 LinkedHashMap 的 accessOrder 属性值为 true
  2. 重写 removeEldestEntry 方法

下面就是 吹逼 讲解 了。

最近学习 Android,看到 关于 LruCache 的实现,发现其内部实际上核心就是维护了一个 LinkedHashMap,于是乎,去查了查 API,发现了一个有意思的方法:

protected boolean removeEldestEntry(Map.Entry<K,V> eldest)

官方文档中写道:

Returns true if this map should remove its eldest entry. It provides the implementor with the opportunity to remove the eldest entry each time a new one is added.

This is useful if the map represents a cache: it allows the map to reduce memory consumption by deleting stale entries.

这不正是我们实现 LruCache 所需要的么,”超过容量后,添加新内容时,移除’最旧’的那个”。

官方文档中也给出了使用的示例:

private static final int MAX_ENTRIES = 100;

protected boolean removeEldestEntry(Map.Entry eldest) {
    return size() > MAX_ENTRIES;
}

//保证有 100 个 entrys,超过时,删掉最早加入的那个。

有了这个方法,我们只需要再做一些补充就大功告成了。

下面需要补充一些 LinkedHashMap 的知识。

至于详细分析,推荐一篇文章 https://blog.csdn.net/justloveyou_/article/details/71713781,分析的很清楚。

LinkedHashMap 有这样一个构造函数:

public LinkedHashMap(int initialCapacity, float loadFactor,boolean accessOrder)

这个 accessOrder 参数控制了迭代顺序,true 时表示按照访问顺序迭代,false 时表示按照插入顺序迭代。

简单来说,原本 LinkedHashMap 遍历顺序为 A->B->C,此时访问 B,再添加 D

  • 当 accessOrder 为 true 时,遍历顺序变为 A->C->B->D
  • 当 accessOrder 为 false 时,遍历变为 A->B->C->D

因此我们实现 LruCache 只需要再令 accessOrder = true 即可,此时最新访问/添加的永远在最尾端,结合 removeEldestEntry 方法,当到达容量时,优先删除最久未访问的内容。


代码

class LRUCache {

    //看了看提交记录里最快的解法,发现可以直接让 LRUCache 类继承 LinkedHashMap
    //不需要像我这里,重新定义一个类,然后再通过组合的方式使用。
    //属实汗颜 = = ,

    //个人看法,尽量避免对给出的类做改动
    //毕竟实际运用时,别人就给一个这样的类,我们擅自改动这个类的继承关系难免会引起不必要的麻烦

    MapCache cache;
    public LRUCache(int capacity) {
        cache = new MapCache(capacity);
    }

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

    public void put(int key, int value) {
        cache.put(key,value);
    }
}

class MapCache extends LinkedHashMap<Integer, Integer> {
    private int MAX;

    //调用父类构造函数,另 accessOrder = true
    MapCache(int max){
        super(max,0.75f,true);
        this.MAX = max;
    }

    protected boolean removeEldestEntry(Map.Entry<Integer,Integer> eldest){
        return size() > MAX;
    }
}

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache obj = new LRUCache(capacity);
 * int param_1 = obj.get(key);
 * obj.put(key,value);
 */

猜你喜欢

转载自blog.csdn.net/whdAlive/article/details/81411800