【问题标题】:Use LinkedHashMap to implement LRU cache使用LinkedHashMap实现LRU缓存
【发布时间】:2015-02-13 01:29:33
【问题描述】:

我试图使用 LinkedHashMap 实现 LRU 缓存。 在 LinkedHashMap (http://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashMap.html) 的文档中,它说:

请注意,如果将键重新插入到地图中,插入顺序不会受到影响。

但是当我执行以下操作时

public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    private int size;

    public static void main(String[] args) {
        LRUCache<Integer, Integer> cache = LRUCache.newInstance(2);
        cache.put(1, 1);
        cache.put(2, 2);
        cache.put(1, 1);
        cache.put(3, 3);

        System.out.println(cache);
    }

    private LRUCache(int size) {
        super(size, 0.75f, true);
        this.size = size;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > size;
    }

    public static <K, V> LRUCache<K, V> newInstance(int size) {
        return new LRUCache<K, V>(size);
    }

}

输出是

{1=1, 3=3}

这表明重新插入确实影响了订单。 有人知道解释吗?

【问题讨论】:

  • 我想知道,您这样做是为了特定目的吗?因为Java 已经提供了一个WeakHashMap,它提供了这个功能。 docs.oracle.com/javase/7/docs/api/java/util/WeakHashMap.html
  • 如果重新插入不影响订单。顺序应该是 {2=2, 3=3},因为 {1=1} 先添加并重新插入。
  • @Jack WeakHashMap 不会像你 think it does 那样做。它与LRU cache 不同。
  • @Jeffrey:它完全符合我的想法。它提供了一种数据结构,允许存储缓存的值,而不必担心如果它们没有在代码中的其他任何地方引用,它们就会被清除。这是一种实现 LRU 缓存的垃圾收集方式。如果您不需要擦除旧值(用于刷新问题,这就是我所说的特定目的),那么它通过允许 Java 在出现时释放它们来解决这个问题是必须的。
  • @Jack 我正在实现这个以进行编码练习。如果我使用 LRU 来保存临时对象并让 GC 处理所有事情,我认为使用 weakHashMap 是一种更好的方法。谢谢

标签: java insert linkedhashmap lru


【解决方案1】:

As pointed out by Jeffrey,您正在使用 accessOrder。创建 LinkedHashMap 时,第三个参数指定如何更改顺序。

"true for access-order, false for insertion-order"

LRU更详细的实现可以看这个 http://www.programcreek.com/2013/03/leetcode-lru-cache-java/

【讨论】:

  • 该链接没有使用 LinkedHashMap 的解决方案。
【解决方案2】:

但您使用的不是广告订单,而是access order

迭代顺序是其条目最后的顺序 访问,从最近最少访问到最近访问(访问顺序)

...

调用 put 或 get 方法会导致访问 对应条目

所以这是你修改缓存时的状态:

    LRUCache<Integer, Integer> cache = LRUCache.newInstance(2);
    cache.put(1, 1); // { 1=1 }
    cache.put(2, 2); // { 1=1, 2=2 }
    cache.put(1, 1); // { 2=2, 1=1 }
    cache.put(3, 3); // { 1=1, 3=3 }

【讨论】:

  • 最后一行的2去哪儿了?
  • LRUCache 只能容纳 2 个元素。由于 2 是最近最少使用的元素,所以当我们添加 3 时,它会被推出缓存。
【解决方案3】:

这是我在 AccessOrder 中使用 LinkedHashMap 的实现。它将最新访问的元素移动到前面,这只会产生 O(1) 开销,因为底层元素组织在一个双向链表中,同时也由哈希函数索引。所以 get/put/top_newest_one 操作都花费 O(1)。

class LRUCache extends LinkedHashMap<Integer, Integer>{
    private int maxSize;
    public LRUCache(int capacity) {
        super(capacity, 0.75f, true);
        this.maxSize = capacity;
    }

    //return -1 if miss
    public int get(int key) {
        Integer v = super.get(key);
        return v == null ? -1 : v;
    }

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

    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        return this.size() > maxSize; //must override it if used in a fixed cache
    }
}

【讨论】:

  • a fixed cache 是什么意思?在这种情况下有哪些缓存?
【解决方案4】:

我使用了以下代码及其作品!!!! 我将窗口大小设为 4,但可以取任何值。

对于广告订单:
1:检查密钥是否存在。

2:如果是,则将其删除(通过使用 lhm.remove(key))

3:添加新的键值对。

访问顺序:

无需删除键,只需 put 和 get 语句自动完成所有操作。

此代码用于访问订单:

import java.util.LinkedHashMap;

public class LRUCacheDemo {

 public static void main(String args[]){

  LinkedHashMap<String,String> lhm = new LinkedHashMap<String,String>(4,0.75f,true) {

     @Override
     protected boolean removeEldestEntry(Map.Entry<String,String> eldest) {
         return size() > 4;
     }
 };
 lhm.put("test", "test");
 lhm.put("test1", "test1");
 lhm.put("1", "abc");
 lhm.put("test2", "test2");
 lhm.put("1", "abc");
 lhm.put("test3", "test3");
 lhm.put("test4", "test4");
 lhm.put("test3", "test3");
 lhm.put("1", "abc");
 lhm.put("test1", "test1");

 System.out.println(lhm);
}
}

【讨论】:

    【解决方案5】:

    技术上 LinkedHashMap 具有以下构造函数。这有助于我们使访问顺序为真/假。如果为假,则保持插入顺序。

    LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
    
    (#Constructs an empty LinkedHashMap instance with the specified initial capacity, load factor and ordering mode)
    

    以下是LRU Cache的简单实现---

      class LRUCache {
    
         private LinkedHashMap<Integer, Integer> linkHashMap;
    
         public LRUCache(int capacity) {
            linkHashMap = new LinkedHashMap<Integer, Integer>(capacity, 0.75F, true) {
              @Override
              protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
                 return size() > capacity;
              }
           };
         }
    
         public void put(int key, int value) {
            linkHashMap.put(key, value);
         }
    
         public int get(int key) {
           return linkHashMap.getOrDefault(key, -1);
         }
    
     } 
    

    【讨论】:

      【解决方案6】:
      I also implement LRU cache with little change in code. I have tested and it works perfectly as concept of LRU cache.
      
      package com.first.misc;
      import java.util.LinkedHashMap;
      import java.util.Map;
      
      public class LRUCachDemo {
       public static void main(String aa[]){
           LRUCache<String, String> lruCache = new LRUCache<>(3);
           lruCache.cacheable("test", "test");
           lruCache.cacheable("test1", "test1");
           lruCache.cacheable("test2", "test2");
           lruCache.cacheable("test3", "test3");
           lruCache.cacheable("test4", "test4");
           lruCache.cacheable("test", "test");
      
      
           System.out.println(lruCache.toString());
       }
      }
      
      class LRUCache<K, T>{
          private Map<K,T> cache;
          private int windowSize;
      
          public LRUCache( final int windowSize) {
              this.windowSize = windowSize;
              this.cache = new LinkedHashMap<K, T>(){
      
                  @Override
                  protected boolean removeEldestEntry(Map.Entry<K, T> eldest) {
                      return size() > windowSize;
                  }
              };
      
          }
      
      
          // put data in cache
          public void cacheable(K key, T data){
              // check key is exist of not if exist than remove and again add to make it recently used
              // remove element if window size is exhaust
              if(cache.containsKey(key)){
                  cache.remove(key);
              }
      
              cache.put(key,data);
      
          }
      
          // evict functioanlity
      
          @Override
          public String toString() {
              return "LRUCache{" +
                      "cache=" + cache.toString() +
                      ", windowSize=" + windowSize +
                      '}';
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2012-08-15
        • 2023-03-26
        • 1970-01-01
        • 2021-03-05
        • 2010-11-03
        • 2015-07-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多