LinkedHashMap源码学习

1. 概述

2,类图

3. 属性

4,构造方法

5. 创建节点

6,转换成 Set/CollectionkeySet()

7. 清空clear()


1. 概述

HashMap 提供的访问,是无序的。而在一些业务场景下,我们希望能够提供有序访问的 HashMap 。那么此时,我们就有两种选择:

TreeMap :按照 key 的顺序。

LinkedHashMap :按照 key 的插入和访问的顺序。

2,类图

 

3. 属性

 

可以看到 LinkedHashMap 实现了自定义的节点 Entry ,一个支持指向前后节点的 Node 子类。代码如下:

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);
        }
    }
  • before 属性,指向前一个节点。after 属性,指向后一个节点。
  • 通过 before + after 属性,我们就可以形成一个以 Entry 为节点的链表

既然 LinkedHashMap 是 LinkedList + HashMap 的组合,那么必然就会有头尾节点两兄弟。所以属性如下:

 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;

head + tail 属性,形成 LinkedHashMap 的双向链表。而访问的顺序,就是 head => tail 的过程

accessOrder 属性,决定了 LinkedHashMap 的顺序

        true 时,当 Entry 节点被访问时,放置到链表的结尾,被 tail 指向。

                              false 时,当 Entry 节点被添加时,放置到链表的结尾,被 tail 指向。如果插入的 key 对应的 Entry 节点已经存在,也会被放到结尾。

4,构造方法

LinkedHashMap 一共有 5 个构造方法,其中四个和 HashMap 相同,只是多初始化 accessOrder = false 。所以,默认使用插入顺序进行访问。

另外一个 LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) 构造方法,允许自定义 accessOrder 属性。代码如下:

public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

5. 创建节点

插入 key-value 键值对时,例如说 #put(K key, V value) 方法,如果不存在对应的节点就会调用newNode方法创建节点。

因为 LinkedHashMap 自定义了 Entry 节点,所以必然需要重写该方法

 Node<K,V> newNode(int hash, K key, V value, Node<K,V> e) {
        LinkedHashMap.Entry<K,V> p =   //创建 Entry 节点
            new LinkedHashMap.Entry<K,V>(hash, key, value, e);
        linkNodeLast(p);  //添加到结尾
        return p;    // 返回
    }

调用 linkNodeLast(LinkedHashMap.Entry<K,V> p) 方法,添加到结尾。代码如下:

 private void linkNodeLast(LinkedHashMap.Entry<K,V> p) {
        LinkedHashMap.Entry<K,V> last = tail;  // 记录原尾节点到 last 中
        tail = p;  // 设置 tail 指向 p ,变更新的尾节点
        if (last == null)
            head = p;  // 如果原尾节点 last 为空,说明 head 也为空,所以 head 也指向 p
        else {  // last <=> p ,相互指向
            p.before = last;
            last.after = p;
        }
    }

6,转换成 Set/CollectionkeySet()

keySet() 方法,获得 key Set 。代码如下:

public Set<K> keySet() {
        Set<K> ks = keySet;   // 获得 keySet 缓存
        if (ks == null) {   // 如果不存在,则进行创建
            ks = new LinkedKeySet();  // LinkedKeySet 是 LinkedHashMap 自定义的
            keySet = ks;
        }
        return ks;
    }

其中, LinkedKeySet 是 LinkedHashMap 自定义的 Set 实现类。代码如下:

 final class LinkedKeySet extends AbstractSet<K> {
        public final int size()                 { return size; }
        public final void clear()               { LinkedHashMap.this.clear(); }
        public final Iterator<K> iterator() {
            return new LinkedKeyIterator();
        }
        public final boolean contains(Object o) { return containsKey(o); }
        public final boolean remove(Object key) {
            return removeNode(hash(key), key, null, false, true) != null;
        }
        public final Spliterator<K> spliterator()  {
            return Spliterators.spliterator(this, Spliterator.SIZED |
                                            Spliterator.ORDERED |
                                            Spliterator.DISTINCT);
        }
        public final void forEach(Consumer<? super K> action) {
            if (action == null)
                throw new NullPointerException();
            int mc = modCount;
            for (LinkedHashMap.Entry<K,V> e = head; e != null; e = e.after)
                action.accept(e.key);
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }

7. 清空clear()

clear() 方法,清空 LinkedHashMap 。代码如下:

public void clear() {
    // 清空
    super.clear();
    // 标记 head 和 tail 为 null
    head = tail = null;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ehdjsbs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值