使用get时LRUCache条目重新排序

时间:2017-07-13 07:26:51

标签: java android linkedhashmap android-lru-cache

我检查了LRUCache的官方Android文档,其中说:每次访问一个值时,它都会移动到队列的头部。将值添加到完整缓存时,该队列末尾的值将被逐出,并且可能符合垃圾回收的条件。 我想这是双链表,它由缓存使用的linkedhashmap维护。为了检查这种行为,我检查了LruCache的源代码,并检查了 get(K key)方法。它进一步调用map的get方法,该方法从底层hashmap获取值并调用recordAccess方法。

public V get(Object key) {
    LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);
    if (e == null)
        return null;
    e.recordAccess(this);
    return e.value;
}

recordAccess方法依次将访问的条目移动到列表的末尾,以防accessOrder设置为true(对于我的问题,我们假设它是),否则它什么都不做。

/**
     * This method is invoked by the superclass whenever the value
     * of a pre-existing entry is read by Map.get or modified by Map.set.
     * If the enclosing Map is access-ordered, it moves the entry
     * to the end of the list; otherwise, it does nothing.
     */
    void recordAccess(HashMap<K,V> m) {
        LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
        if (lm.accessOrder) {
            lm.modCount++;
            remove();
            addBefore(lm.header);
        }
    }

这听起来与上述声明相矛盾,因为它说元素被移动到队列的头部。相反,它移动到列表的最后一个元素(使用head.before)。当然,我在这里错过了什么,有什么帮助吗?

2 个答案:

答案 0 :(得分:2)

您没有遗漏任何内容,只是您正在阅读LruCache的{​​{1}}文档。 LinkedHashMap有自己的文档,特别是有关accessOrder的文档。 (在Java docs上相同)。

  

[...当accessOrder = true时...]迭代顺序是上次访问其条目的顺序,从最近访问到最近访问(访问顺序)

因此LinkedHashMap将最近使用的条目放在最后,并记录在案。

实际上LinkedHashMap描述了这种缓存在理论上是如何工作的,但是LruCache显示了如何在不添加单独的向后移动迭代器的情况下实现它:通过将最近的元素放在最后,trimming可以使用已经可用的(向前移动的)迭代器来有效地访问(和移除)旧元素。

虽然在这里,现在我无法分辨removeEldestEntry的错误。也许它在过去并不存在。

答案 1 :(得分:1)

来自LinkedHashMap的javadoc:

  

如果使用了三个参数构造函数,并且 accessOrder 被指定为 true ,则迭代将按访问条目的顺序进行。访问顺序受 put get putAll 操作的影响,但不受集合视图上的操作影响。

Exactly the caseLruCache有。

public LruCache(int maxSize) {
    if (maxSize <= 0) {
        throw new IllegalArgumentException("maxSize <= 0");
    }
    this.maxSize = maxSize;
    this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}

让我们看看recordAccess()做了什么:

    void recordAccess(HashMap<K,V> m) {
        LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
        if (lm.accessOrder) { // true, because `LruCache` instantiated this 
                              // map with `accessOrder = true`
            lm.modCount++;
            remove(); // remove this `LinkedHashMapEntry` from the map
            addBefore(lm.header); // adds this entry before the current header of
                                  // the map, thus this entry becomes the header
        }
    }
  

相反,它被移动到列表的最后一个元素(使用head.before)。

我看不出你的陈述是如何有效的。