Изменение порядка ввода LRUCache при использовании get


Я проверил официальную документацию Android для LRUCache, которая гласит : Каждый раз, когда значение обращается, оно перемещается в начало очереди. Когда значение добавляется в полный кэш, значение в конце этой очереди удаляется и может стать пригодным для сборки мусора . Я полагаю, что это двусвязный список, который поддерживается linkedhashmap, который используется кэшем. Чтобы проверить это поведение, я проверил исходный код LruCache и проверил get (K ключ) метод. Далее он вызывает метод get map, который получает значение из базового 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.до). Конечно, я что-то упускаю, какая-нибудь помощь?

2 8

2 ответа:

Вы ничего не упускаете, просто Вы читаете LruCache документацию для LinkedHashMap. LinkedHashMap имеет свою собственную документацию, в частности о своем accessOrder. (То же самое на Java docs).

[...когда accessOrder=true...] порядок итерации - это порядок, в котором его записи были доступны в последний раз, от наименее недавно доступного до самого последнего (порядок доступа)

Таким образом, LinkedHashMap помещает последние использованные записи в конец, и это зарегистрированный.

Практически LruCache описывает, как такой кэш работает в теории, но LinkedHashMap показывает, как реализовать его без добавления отдельных итераторов с обратным движением: помещая последние элементы в конец, обрезка может использовать уже имеющийся итератор (с прямым движением) для эффективного доступа (и удаления) старых элементов.

Хотя здесь и сейчас я не мог сказать, что было не так.removeEldestEntry. Возможно, в прошлом его не существовало.

Из явадока LinkedHashMap:

Если используется конструктор с тремя аргументами и accessOrder задается как true, итерация будет выполняться в том порядке, в котором были получены записи. На порядок доступа влияет put, get и putAll операции, но не операции над представлениями коллекции.

Именно тот случай , который имеет LruCache.

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.до).

Я не могу видеть, как ваше заявление является действительным.