源码分析-LinkedHashMap

继承关系

源码分析-LinkedHashMap_第1张图片

通过上图可以看出LinkedHashMap直接继承了HashMap接口,实现了Map接口,间接实现了CloneableSerializable接口

构造方法

LinkedHashMap拥有四个构造方法,由于直接继承了 HashMap,具体实现由此父类决定

在这几个构造方法之前存在一个重要的属性accessOrder,其决定了在迭代LinkedHashMap时是以访问顺序还是以插入顺序进行

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

HashMapNode是用来存储数据的,在LinkedHashMap中对此结构进行了拓展,新增了before和after,以双向链表的形式存储数据

    static class Entry extends HashMap.Node {
        // 双向链表的前驱和后继
        Entry before, after;
        Entry(int hash, K key, V value, Node next) {
            // 调用HashMap中Node的构造方法
            super(hash, key, value, next);
        }
    }
  1. LinkedHashMap()
    // 调用了HashMap的无参构造方法,并设置为以插入顺序进行迭代
    public LinkedHashMap() {
        super();
        accessOrder = false;
    }
  1. LinkedHashMap(int initialCapacity)
    // 设置Map的的初始值,并由HashMap具体实现
    public LinkedHashMap(int initialCapacity) {
        super(initialCapacity);
        accessOrder = false;
    }
  1. LinkedHashMap(int initialCapacity, float loadFactor)
    // 设置Map的初始值和加载因子
    public LinkedHashMap(int initialCapacity, float loadFactor) {
        super(initialCapacity, loadFactor);
        accessOrder = false;
    }
  1. LinkedHashMap(Map m)
    // 合并两个Map,调用父类的putMapEntries()方法进行实现
    public LinkedHashMap(Map m) {
        super();
        accessOrder = false;
        putMapEntries(m, false);
    }

添加数据

在对LinkedHashMap执行put()方法时,实际上是调用HashMap的put()方法,由于在上篇文章中已经说过这部分代码,这里只对关键代码进行解释

    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
        HashMap.Node[] tab;
        HashMap.Node p;
        int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            // 进行第一次初始化
            n = (tab = resize()).length;
        // 判断是否存在当前元素
        if ((p = tab[i = (n - 1) & hash]) == null)
            // 不存在则进行插入,这里利用多态性调用LinkedHashNap的newNode()方法
            tab[i] = newNode(hash, key, value, null);
        else {
            ...
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

调用newNode()

    Node newNode(int hash, K key, V value, Node e) {
        // 创建一个新节点
        LinkedHashMap.Entry p = new LinkedHashMap.Entry(hash, key, value, e);
        linkNodeLast(p);
        // 并返回当前元素
        return p;
    }

    private void linkNodeLast(LinkedHashMap.Entry p) {
        // tail表示双向链表最末尾的元素;在进行第一次添加时tail为null,先把tail赋值给last节点,此时last也为Null
        LinkedHashMap.Entry last = tail;
        // 将新元素添加到队列末尾
        tail = p;
        // 当第一次添加数据时,将第一个元素设置为头节点
        if (last == null)
            head = p;
        else {
            // 若不是第一次添加
            
            // 修改当前元素的前驱指向
            p.before = last;
            // 修改最后一个元素的后继指向
            last.after = p;
        }
    }

至此,向Map中添加新元素的工作已经完成。在这个过程中,数据存储在Node数组中,Entry保存了插入顺序。

获取数据

获取数据主要由HashMap实现,这部分内容已经在上篇文章中讲过,不做赘述

    public V get(Object key) {
        Node e;
        // 通过HashMap的getNode()方法获取数据
        if ((e = getNode(hash(key), key)) == null)
            return null;
        // 如果为true 将获取到的元素e放到末尾
        if (accessOrder)
            afterNodeAccess(e);
        // 获取值
        return e.value;
    }

总结

通过LinkedHashMap源码分析发现,该数据类型是通过内部的Entry记录数据的插入顺序并组成双向链表。底层存储数据的依旧是数组、单向链表和红黑树。也发现了与HashMap存在的异同。

相同点:

  • 允许一条null Key的数据和多条null Value的数据
  • 底层存储数据的结构不变
  • 都是线程不安全的

不同点:

  • HashMap是无序的,LinkedHashMap是有序的

阅读原文

你可能感兴趣的:(源码分析-LinkedHashMap)