LinkedHashMap的实现原理

14 阅读2分钟
  • 继承了HashMap,并重写了hashMap的 newNode(hash,key,value,null),newTreeNode(hash,key,value,next) 方法。 image.png

  • 当accessOrder为false时(默认值),LinkedHashMap按照元素的插入顺序进行遍历。此时,双向链表的作用主要是记录元素的插入顺序,使得LinkedHashMap能够像List那样按照元素插入的顺序进行迭代。

  • 当accessOrder为true时,LinkedHashMap则会按照元素的访问顺序进行遍历。这意味着,每次访问(通过get或put方法)一个元素时,该元素都会被移动到双向链表的末尾。这样,最近访问的元素就会出现在链表的末尾,而最久未访问的元素则会出现在链表的开头。

   
    Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMapEntry<K,V> p =
            new LinkedHashMapEntry<>(hash, key, value, e);
        linkNodeLast(p);
        return p;
    }

  TreeNode<K,V> newTreeNode(int hash, K key, V value, Node<K,V> next) {
        TreeNode<K,V> p = new TreeNode<>(hash, key, value, next);
        linkNodeLast(p);
        return p;
    }
    //双向链表添加数据
    private void linkNodeLast(LinkedHashMapEntry<K,V> p) {
        LinkedHashMapEntry<K,V> last = tail;
        tail = p;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
    }


  • 继承了HashMap ,并重写afterNodeAccess()方法,e表示当前要插入的新节点在hashMap中是存在,已经进行了旧值替换新值,现在要做的就是把 e的前一个节点的after指向e的后一个节点,e的后一个节点的before指向e的前一个节点,然后e的before指向last尾部节点,last尾部节点的after指向e节点,此时e节点 变成tail节点.
 //调整调整双向链表中的节点顺序,把e节点放到双向链表的尾部
 void afterNodeAccess(Node<K,V> e) { // move node to last
        LinkedHashMapEntry<K,V> last;
        if (accessOrder && (last = tail) != e) {
            LinkedHashMapEntry<K,V> p =   (LinkedHashMapEntry<K,V>)e;
             LinkedHashMapEntry<K,V>    b = p.before;//e节点的前一个节点
               LinkedHashMapEntry<K,V>  a = p.after;//e节点的后一个节点
            p.after = null;
            if (b == null)
                head = a;
            else
                b.after = a;//
            if (a != null)
                a.before = b;
            else
                last = b;
            if (last == null)
                head = p;
            else {
                p.before = last;
                last.after = p;
            }
            tail = p;
            ++modCount;
        }
    }

  • 当调用get方法获取元素时,也会调用afterNodeAccess(e),把当前对象移动到链表的尾部
    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;
    }