LinkedHashMap 源码分析-访问最少删除策略

105 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情

LinkedHashMap 源码分析-访问最少删除策略

访问最少删除策略也叫做 LRU(Least recently used,最近最少使用),意思就是经常访问的元素会移动到队尾,不经常访问的数据会在队头的位置,然后可以通过设置的删除策略,比如当元素大于多少个时,把最少使用的元素进行删除。

元素访问与转移

如何实现在访问时,元素被移动到队尾? 源码:

public V get(Object key) {
    Node<K,V> e;
    if ((e = getNode(hash(key), key)) == null)
        return null;
    if (accessOrder)
        afterNodeAccess(e);
    return e.value;
}

if ((e = getNode(hash(key), key)) == null) return null;调用了 HashMap 的 get 方法

if (accessOrder) afterNodeAccess(e);如果设置了 LRU 策略,则会把当前 key 移动到队列的尾部

主要是 通过 afterNodeAccess 方法把当前访问节点移动到了队尾

具备相同性质的方法还有:

    public V getOrDefault(Object key, V defaultValue) {
       Node<K,V> e;
       if ((e = getNode(hash(key), key)) == null)
           return defaultValue;
       if (accessOrder)
           afterNodeAccess(e);
       return e.value;
   }
    default V computeIfAbsent(K key,
            Function<? super K, ? extends V> mappingFunction) {
        Objects.requireNonNull(mappingFunction);
        V v;
        if ((v = get(key)) == null) {
            V newValue;
            if ((newValue = mappingFunction.apply(key)) != null) {
                put(key, newValue);
                return newValue;
            }
        }

        return v;
    }
    public V compute(K key,
                     BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
        if (remappingFunction == null)
            throw new NullPointerException();
        int hash = hash(key);
        Node<K,V>[] tab; Node<K,V> first; int n, i;
        int binCount = 0;
        TreeNode<K,V> t = null;
        Node<K,V> old = null;
        if (size > threshold || (tab = table) == null ||
            (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((first = tab[i = (n - 1) & hash]) != null) {
            if (first instanceof TreeNode)
                old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
            else {
                Node<K,V> e = first; K k;
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k)))) {
                        old = e;
                        break;
                    }
                    ++binCount;
                } while ((e = e.next) != null);
            }
        }
        V oldValue = (old == null) ? null : old.value;
        V v = remappingFunction.apply(key, oldValue);
        if (old != null) {
            if (v != null) {
                old.value = v;
                afterNodeAccess(old);
            }
            else
                removeNode(hash, key, null, false, true);
        }
        else if (v != null) {
            if (t != null)
                t.putTreeVal(this, tab, hash, key, v);
            else {
                tab[i] = newNode(hash, key, v, first);
                if (binCount >= TREEIFY_THRESHOLD - 1)
                    treeifyBin(tab, hash);
            }
            ++modCount;
            ++size;
            afterNodeInsertion(true);
        }
        return v;
    }

这些方法都是对于元素在访问时,不断的把访问的经常访问的节点移动到队尾,那么靠近队头的节点,自然就是很少被访问的元素了。

删除

void afterNodeInsertion(boolean evict) { 
    LinkedHashMap.Entry<K,V> first;
    if (evict && (first = head) != null && removeEldestEntry(first)) {
        K key = first.key;
        removeNode(hash(key), key, null, false, true);
    }
}

LinkedHashMap.Entry<K,V> first; 得到元素头节点

if (evict && (first = head) != null && removeEldestEntry(first)) removeEldestEntry()控制删除策略,如果队列不为空,并且删除策略允许删除的情况下,删除头节点

removeNode(hash(key), key, null, false, true); 删除头节点