LinkedHashMap 源码分析-访问

401 阅读2分钟

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

LinkedHashMap 源码分析-

HashMap 是无序的,TreeMap 是根据 key 进行排序,而LinkedHashMap,是根据插入顺序排序的。

public class LinkedHashMap<K,V>
    extends HashMap<K,V>
    implements Map<K,V>
{
......
}

LinkedHashMap 继承了 HashMap ,所以它拥有 HashMap 的所有特性,额外的,LinkedHashMap还包含了两大特性:

  1. 按照插入顺序进行访问;
  2. 实现了访问最少最先删除功能,把很久都没有访问的 key 自动删除。

LinkedHashMap 链表结构

LinkedHashMap把 Map 的元素都串联起来,形成了一个链表,而链表可以保证顺序,如此就实现了按插入顺序访问。

    static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }

    private static final long serialVersionUID = 3801124242820219131L;

    /**
     * The head (eldest) of the doubly linked list.
     */
    transient LinkedHashMap.Entry<K,V> head;

    /**
     * The tail (youngest) of the doubly linked list.
     */
    transient LinkedHashMap.Entry<K,V> tail;

    /**
     * The iteration ordering method for this linked hash map: <tt>true</tt>
     * for access-order, <tt>false</tt> for insertion-order.
     *
     * @serial
     */
    final boolean accessOrder;

transient LinkedHashMap.Entry<K,V> head;链表头

transient LinkedHashMap.Entry<K,V> tail;链表尾

static class Entry<K,V> extends HashMap.Node<K,V> {......} 继承 Node,数组中元素属性增加了 before 和 after 属性

final boolean accessOrder; 控制两种访问模式的字段,true 按照访问顺序,会把经常访问的 key 放到队尾,false 按照插入顺序提供访问

按照顺序新增

新增节点,并追加到链表的尾部。 LinkedHashMap 通过新增头节点、尾节点,给每个节点增加 before、after 属性,每次新增时,都把节点追加到尾节点等手段,在新增的时候,就已经维护了按照插入顺序的链表结构了。

    private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        LinkedHashMap.Entry<K,V> last = tail;
        tail = p;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
    }

tail = p;新增节点等于位节点

if (last == null) head = p; last 为空,说明链表为空,首尾节点相等

else { p.before = last; last.after = p; }链表有数据,直接建立新增节点和上个尾节点之间的前后关系即可

按照顺序访问

LinkedHashMap 只提供了单向遍历访问,按照插入的顺序从头到尾进行访问。

通过迭代器进行访问,迭代器初始化的时候,默认从头节点开始访问,在迭代的过程中,不断访问当前节点的 after 节点。

        LinkedHashIterator() {
            next = head;
            expectedModCount = modCount;
            current = null;
        }

        final LinkedHashMap.Entry<K,V> nextNode() {
            LinkedHashMap.Entry<K,V> e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            current = e;
            next = e.after;
            return e;
        }

next = head;头节点作为第一个访问的节点

if (modCount != expectedModCount) throw new ConcurrentModificationException(); 校验

next = e.after; 通过链表的 after 结构,找到下一个迭代的节点