【Java集合】HashMap的putMapEntries()源码详解

在HashMap的四个构造方法中,有一个构造方法传入的参数是一个Map类型的对象

/**
 * 包含另一个“Map”的构造函数,包含另一个Map的映射,如果被映射的Map是一个null会抛出空指针异常。负载因子是默认的
 * 直接传入存储了要添加进HashMap的key-value对的map,来构造HashMap
 */
public HashMap(Map<? extends K, ? extends V> m) {
	//将默认的负载因子赋值给成员变量loadFactor
    this.loadFactor = DEFAULT_LOAD_FACTOR;
	//调用PutMapEntries()来完成HashMap的初始化赋值过程
    putMapEntries(m, false);//以后的文章中会分析到这个方法
}

下面我们分析一下在传入Map参数的构造方法中调用的putMapEntries()方法。这个方法调用了HashMap的resize()扩容方法和putVal()存入数据方法。

putMapEntries方法会被HashMap的拷贝构造函数public HashMap(Map<? extends K, ? extends V> m)或者Map接口的putAll函数(被HashMap给实现了)调用到。该函数使用的是默认修饰符(default),也就是只有包访问权限,只能被本类或者该包下的类访问到,所以一般情况下用户无法调用。

/**
 * 该方法的作用:将传入的子Map中的全部元素逐个添加到HashMap中
 * @param evict 最初构造此Map时为false,否则为true(中继到afterNodeInsertion方法)。
 */
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
    //获得参数Map的大小,并赋值给s
    int s = m.size();
    // 判断大小是否大于0  只有大于零Map中才有元素来插入HashMap
    if (s > 0) {
        // 判断table是否已经初始化  如果table=null一般就是构造函数来调用的putMapEntries,或者构造后还没放过任何元素
        if (table == null) { // pre-size
            // 如果未初始化,则计算HashMap的最小需要的容量(即容量刚好不大于扩容阈值)。这里Map的大小s就被当作HashMap的扩容阈值,然后用传入Map的大小除以负载因子就能得到对应的HashMap的容量大小(当前m的大小 / 负载因子 = HashMap容量)
            // 先不考虑容量必须为2的幂,那么下面括号里会算出来一个容量,使得size刚好不大于阈值。但这样会算出小数来,但作为容量就必须向上取整,所以这里要加1。此时ft可以临时看作HashMap容量大小
            float ft = ((float)s / loadFactor) + 1.0F;
            //比较最大容量与ft,取小值; 到这里t暂时表示HashMap的容量大小。如果是将ft浮点型赋值给t整形,因为前面加了1.0f,这里也就实现了向上取整
            int t = ((ft < (float)MAXIMUM_CAPACITY) ?
                     (int)ft : MAXIMUM_CAPACITY);
            // 只有在算出来的容量t > 当前暂存的容量(容量可能会暂放到阈值上的,刚使用构造函数构造出来的HashMap并且没有存入元素时,容量大小就会被暂时存在threshold中)时
            // 才会用t计算出新容量,暂时存放到阈值上,在后面触发resize()扩容的时候会对threshold重新计算正确的阈值
            if (t > threshold)
                threshold = tableSizeFor(t);
        }
        //如果当前Map已经初始化,且这个map中的元素个数大于扩容的阀值就得扩容
        //这种情况属于预先扩大容量,再put元素
        else if (s > threshold)
            resize();
        //遍历map,将map中的key和value都添加到HashMap中
        for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
            K key = e.getKey();
            V value = e.getValue();
            // 调用HashMap的put方法的具体实现方法putVal来对数据进行存放。该方法的具体细节在后面会进行讲解
            // putVal可能也会触发resize
            putVal(hash(key), key, value, false, evict);
        }
    }
}

 注释里已经解释得很清楚了,这里再提几点重要的:

  1. if (table == null)分支,说明是HashMap的拷贝构造函数来调用的putMapEntries,或者是构造以后还没有放过任何元素,然后再调用putAll。
  2. float ft = ((float)s / loadFactor) + 1.0F这里的加1是因为,size / loadFactor = capacity,但如果算出来的capacity是小数,却又向下取整,会造成容量不够大,所以,如果是小数的capacity,那么必须向上取整。
  3. 算出来的容量必须小于最大容量MAXIMUM_CAPACITY,否则直接让capacity等于MAXIMUM_CAPACITY
  4. if (t > threshold)这里的threshold成员实际存放的值是capacity的值。因为在table还没有初始化时(table还是null),用户给定的capacity会暂存到threshold成员上去(毕竟HashMap没有一个成员叫做capacity,capacity是作为table数组的大小而隐式存在的)。
  5. else if (s > threshold)说明传入map的size都已经大于当前map的threshold了,即当前map肯定是装不下两个map的并集的,所以这里必须要执行resize操作。
  6. 最后循环里的putVal可能也会触发resize操作。

参考资料:https://blog.csdn.net/anlian523/article/details/103639094 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值