【Java Collections】Map-HashMap 源码解析(二)

HashMap 内的主要数据结构

  1. 内部类 Node<K,V>(实现了Map.entry接口,存储key-value的基础类,链表)
  2. table (Node<K,V> 数组)

基本思路是(后续会做更详细的解读):

  1. 根据key的hash值确定table的index索引
  2. table的每个index实际上存储的是Node链表,由第1步确定索引后,再循环链表如果存在相同key(key.equals)则替换Node的value值,否则链表尾添加新Node

构造函数

无参构造函数

threshold 变量为阈值,table大小查过该变量就会触发扩容(resize)
而 threshold = capacity * loadFactor

    public HashMap() {
        // 默认扩容因子0.75
        this.loadFactor = DEFAULT_LOAD_FACTOR; 
    }
    // table 的初始化推迟到了 map.put(key,value) 的resize()时
    newCap = DEFAULT_INITIAL_CAPACITY;
    newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
     /** 省略部分代码 **/
    // 初始化table
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;

Map参数构造函数

public HashMap(Map<? extends K, ? extends V> m) {
        this.loadFactor = DEFAULT_LOAD_FACTOR;// 默认扩容因子0.75
        //主要做两件事情
        //1. 利用上一篇文章提到的 求2的幂 的方法,确定HashMap的size
        //2. 将 m 内的key-value(即node)逐个复制到该HashMap中
        putMapEntries(m, false);
    }

初始变量构造函数

public HashMap(int initialCapacity, float loadFactor) {
        // 参数的合法性校验       
         if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                                               initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                                               loadFactor);
        this.loadFactor = loadFactor;
        // threshold 为扩容阈值,超过该值即触发resize
        // tableSizeFor即上篇文章介绍的求2的幂方法
        this.threshold = tableSizeFor(initialCapacity);
    }

// table 的初始化也推迟到了 map.put(key,value) 的resize()时,与无参构造函数不同的是,此处初始化的大小,正是你设置的initialCapacity,而threshold也被重新计算
// 利用threshold做一个存储的过渡,完美做到了一点都不浪费不啰嗦的优良传统
newCap = oldThr;
        if (newThr == 0) {
            float ft = (float)newCap * loadFactor;
            newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                      (int)ft : Integer.MAX_VALUE);
        }
        threshold = newThr;
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
        table = newTab;

// 阿里开发规约建议使用该 初始化函数,设置初始容量,尽可能的避免map.resize带来的性能消耗,也提高存储空间的利用率(用多少声明多少)
public HashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR);
}

小结

综上可以发现,初始化主要是设置table容量相关值,具体的table大小初始化推迟到了putValue阶段,这也就是下篇文章要介绍的重点。

    原文作者:誓词倾城
    原文地址: https://www.jianshu.com/p/5abdc9a966d8
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞