java:了解LRU实现

时间:2013-12-11 18:33:08

标签: java

我试图理解下面的LRU实现。无法弄清楚以下内容:

  1. addFirst()有什么用?正在从putget方法调用此方法。
  2. 双重链表如何帮助跟踪最早的条目?所有条目实际上都在地图中,其中一个条目不知道另一个条目。
  3. 这些问题可能听起来很愚蠢,但我真的很感激解释。

    代码:

    import java.util.HashMap;
    import java.util.Map;
    import java.util.Map.Entry;
    
    
    public class LRUDLL<K, V> {
    
    private final Map<K, Entry<K,V>> map;
    private Entry<K, V> oldest;
    private final int lruSize;
    
    public LRUDLL (int lruSize) {
        if(lruSize <= 0){
            throw new IllegalArgumentException("Size is inappropriate");
        }
        map = new HashMap<K, Entry<K, V>>();
        this.lruSize = lruSize;
    }
    
    
        private static class Entry<K, V> {
            Entry<K, V> left;
            Entry<K, V> right;
            K key;
            V value;
    
            Entry(Entry<K, V> left, K key, V value, Entry<K, V> right) {
                this.left = left;
                this.key = key;
                this.value = value;
                this.right = right;
            }
        };
    
        private void addFirst(Entry<K, V> entry) {
            remove(entry);
            if(oldest == null) {
                entry.left = entry.right = entry;
                oldest = entry;
            } else {
                Entry<K, V> tail = entry;
    
                tail.right = entry;
                entry.left = tail;
    
                //deal with circulating
                oldest.left = entry;
                entry.right = oldest;
            }
        }
    
        private void remove (Entry<K, V> entry) {
            assert entry != null;
    
            if(entry.left != null) entry.left.right = entry.right;
            if(entry.right != null) entry.right.left = entry.left;
            if(entry == oldest) oldest = entry.right;
        }
    
        public synchronized void put (K key, V value) {
            Entry<K, V> entry = new Entry<K, V>(null, key, value, null);
            map.put(key, entry);
            addFirst(entry);
            if(removeOldestEntry()) {
                remove(oldest);
            }
        }
    
        public synchronized V get(K key) {
            Entry<K, V> entry = map.get(key);
            if(entry!= null) {
                addFirst(entry);
                return entry.value;
            }
            return null;
        }
    
        private boolean removeOldestEntry() {
            return map.size() > lruSize;
        }
    }
    

2 个答案:

答案 0 :(得分:1)

  

addFirst()有什么用?从两者调用此方法   放置和获取方法。

最近使用的数据结构需要在访问条目时更新。当您put时,您希望将条目添加到前面,因为它是最近使用的。当你从LRU get输入一个条目时,它也是最近使用过的条目,所以把它带到LRU的前面。

  

双重链表如何帮助追踪最早的条目?一切   条目实际上在一个条目不知道的地图中   另一个。

查看LinkedHashMap的实施情况。双向链表保持访问顺序。每个条目都知道在它之前和之后访问了哪个条目。

答案 1 :(得分:1)

我认为这里的关键点是它不仅仅是一个双向链表,而是一个循环双向链表。因此oldest至少 - 最近使用的元素,oldest.right第二个 - 最近使用的元素,。 。 。 oldest.left - 最近使用过的元素。 (并且oldest.left.left第二大 - 最近使用的元素,依此类推。)

我不确定为什么会这样做 - 似乎更简单的是让oldest指向最近最少使用的newest指向最...最近使用过 - 但这并不重要。

  

addFirst()有什么用?正在从putget方法调用此方法。

addFirst从列表中的当前位置删除指定的条目,并将其添加到oldest的左侧,从而将其标记为最近使用的元素。

  

双重链表如何帮助追踪最早的条目?所有条目实际上都在地图中,其中一个条目不知道另一个条目。

嗯,所以这个实现中存在一个严重的错误:它实际上从来没有从map删除任何元素,所以它实际上不是LRU缓存。更糟糕的是,它永远不会使leftright对它应该“删除”的条目归零,这意味着随后从map检索这些条目,然后重新{{1 - ed,列表序列完全错误。所以实施很糟糕。

假设如何工作是这样的:addFirst只有所有条目,但它不知道哪个是最近最少使用的。该列表按照最近使用的顺序保留所有元素,因此在任何给定时间,map是最近最少使用的。

(该错误的根本原因是oldest被用于两个单独的事情:remove只需要它从列表中的位置删除元素,因此它可以移动到一个新职位,而addFirst实际上需要能够从put中删除oldest。可能map的当前版本应该内联到remove,并且应该创建一个实际删除最旧元素的新addFirst。)