【问题标题】:Implementing LRU Cache in Java在 Java 中实现 LRU 缓存
【发布时间】:2021-01-16 03:32:52
【问题描述】:

问题:定义一个固定大小的 LRU 缓存,支持对 E 类型的对象进行分期 O(1) 操作。每个对象都有一个唯一的 id。本题包含问题描述和几种解决方法:LRU cache in Java with Generics and O(1) operations

不起作用的可能解决方案: 只创建一个链表和一个HashMap。维护 LinkedList 和 HashMap。 hashmap 包含链表中每个节点的地址。

可以通过以下方式从缓存中删除条目:

node = hashmap.get(elem.id);
node.prev.next = node.next;
node.next.prev = node.prev;
hashmap.remove(elem.id)

(为简洁起见,此处忽略极端情况)

这里的问题是 Java 的 LinkedList 类似乎没有公开列表中的各个节点。所以我不能将他们的地址存储在哈希图中。有解决方法吗?大多数现有解决方案都实现了自己的 DoubleLinkedList 类。我可以使用/扩展 Java Collections Framework 中的现有类吗?

【问题讨论】:

  • 如果没有构建自己的链表和哈希映射,您可能无法做到这一点。或者您可以专门为此使用LinkedHashMap constructor

标签: java guava


【解决方案1】:

对于 LRU 缓存,简单的解决方案是使用 LinkedHashMap 并将 accessOrder 设置为 true 创建它。这告诉地图按上次访问时间而不是插入时间对地图条目进行排序。该课程的javadoc 更详细地解释了它的作用。

如果您想实现自动缓存删除,请扩展 LinkedHashMap 并覆盖 removeEldestEntry 以实施您首选的删除策略。该方法的javadoc 有一个示例,将缓存大小限制为固定数量的条目。

您没有问,但是以这种方式实现的 LRU 缓存的复杂性通常与 LinkedHashMap 相同。它将为 O(1)(摊销)用于条目插入、查找和删除。

【讨论】:

  • 假设缓存中的每个元素都有一个固定的优先级,它被存储为一个属性。我想在缓存已满时删除优先级最低的元素。我怎样才能通过覆盖只接受一个参数的受保护布尔 removeEldestEntry​(Map.Entry eldest) 来做到这一点?这是否会改变解决方案的预期运行时复杂性?
  • 嗯,这是一个不同的问题。在 LRU 缓存中,您删除最近最少使用的缓存条目。如果您根据优先级删除元素,则它不是 LRU 缓存。 LinkedHashMap 仅适用于 LRU 和“最旧”的插入时间缓存。 (但是,是的,它改变了任何解决方案的预期运行时复杂性。)
  • 我同意。谢谢。
【解决方案2】:

这里有一个解决方案,它只在 LRU 缓存中实现了 get() 方法,分摊的 O(1) 时间。它还可以在 O(1) 时间内实现 add(E) 和 removeOldest() 操作。唯一的缺点是它在链表中创建了空节点,这会浪费内存。

class LRUCache<E> {
  public E get(String id) {
    if (!addresses.containsKey(id)) return null;
    List<E> valList = addresses.get(id);
    E val = valList.get(0);
    valList.clear();
    List<E> newList = List.of(val);
    list.add(newList);  // O(1) time
    addresses.put(id, newlist);
    return val;
  }

  HashMap<String, List<E>> addresses;
  LinkedList<List<E>> list;
}

【讨论】:

    猜你喜欢
    • 2015-07-19
    • 2021-03-05
    • 2010-09-18
    • 2010-11-03
    • 1970-01-01
    • 2015-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多