最近回顾了下HashMap的源码(JDK1.7),当读到putAll方法时,发现了之前写的TODO标记,当时由于时间匆忙没来得及深究,现在回顾到了就再仔细思考了下

    @Override
public void putAll(Map<? extends K, ? extends V> m) {
int numKeysToBeAdded = m.size();
if (numKeysToBeAdded == 0)
return;
// TODO 这里的numKeysToBeAdded是不是应该要this.size+m.size()呢?
// TODO 这里确实有点问题,下面的for循环中put操作可能会导致再次resize,奇怪怎么没人提出这个问题呢?
if (numKeysToBeAdded > threshold) {
// +1是为了补上被强转为int而抹去的小数部分
int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
if (targetCapacity > MAXIMUM_CAPACITY)
targetCapacity = MAXIMUM_CAPACITY;
int newCapacity = table.length;
while (newCapacity < targetCapacity)
newCapacity <<= 1;
if (newCapacity > table.length)
resize(newCapacity);
} for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
put(e.getKey(), e.getValue());
}

  如注释中所示 numKeysToBeAdded > threshold 就是想提前判断Map是否需要扩容,如果需要的话则直接一步到位,从而防止循环中的put操作引起多次扩容,以次来减小 resize 方法带来的性能开销。

但是:我们看方法的第一行,int numKeysToBeAdded = m.size();  如果要实现扩容一步到位的话,这里的 numKeysToBeAdded 不应该是当前Map的size加m的size之和吗?  this.size + m.size()  > threshold

就扩容才能保证m中所有元素添加到当前HashMap后只触发一次resize 。

  测试代码如下,直接debug HashMap的putAll方法,我们可以看到整个putAll是进行了两次resize

        Map map = new HashMap(4);
Map m = new HashMap(8);
map.put("a", "haha");
map.put("b", "haha");
map.put("c", "haha");
m.put("1", "a");
m.put("2", "a");
m.put("3", "a");
m.put("4", "a");
map.putAll(m);

  JDK1.8的HashMap已经实现已经做了很大的修改,但是当我切换到1.8 debug时还是resize了两次,为什么呢?仔细看下面的注释(当时看源码的时候直接把这段注释忽略了,汗),JDK的大神们给出了如下的解释,显然他们也知道这个问题,但是主要考虑到m和当前的HashMap中可能存在重复的key,这样的话就可能造成HashMap浪费了比较大的空间(画外音:HashMap默认加载因子为0.75的设计初衷不就是采取了空间换时间的思想嚒??)

        /*
* Expand the map if the map if the number of mappings to be added
* is greater than or equal to threshold. This is conservative; the
* obvious condition is (m.size() + size) >= threshold, but this
* condition could result in a map with twice the appropriate capacity,
* if the keys to be added overlap with the keys already in this map.
* By using the conservative calculation, we subject ourself
* to at most one extra resize.
*/

在HashMap中 size  肯定会小于或等于 threshold ,所以putAll时当 m.size() > threshold 进行扩容,HashMap的容量增加至少1倍,则因为存在 m.size() > size 所以就算 m.size() + size > threshold(第一次扩容后) 只要再做一次扩容就可以满足HashMap的规则了。

更全的学习注释可以参考:https://github.com/hiccup234/misc/blob/master/src/main/java/top/hiccup/jdk/container/mycontainer/MyHashMap7.java

Java基础:HashMap中putAll方法的疑惑的更多相关文章

  1. Java基础-hashMap原理剖析

    Java基础-hashMap原理剖析 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   一.什么是哈希(Hash) 答:Hash就是散列,即把对象打散.举个例子,有100000条数 ...

  2. Java基础(中)

    面向对象基础 面向对象和面向过程的区别 两者的主要区别在于解决问题的方式不同: 面向过程把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题. 面向对象会先抽象出对象,然后用对象执行方法的方式 ...

  3. java基础---->hashMap的简单分析(一)

    HashMap是一种十分常用的数据结构对象,可以保存键值对.它在项目中用的比较多,今天我们就来学习一下关于它的知识. HashMap的简单使用 一.hashMap的put和get方法 Map<S ...

  4. Java基础__Java中自定义集合类

    Java基础__Java中集合类 传送门 自定义MyArrayList集合实现:增加数据.取数据.查看集合中数据个数方法 package com.Gary; public class MyArrayL ...

  5. JAVA基础语法:函数(方法)、类和对象(转载)

    4.JAVA基础语法:函数(方法).类和对象 函数 在java中函数也称为方法,是一段具备某种功能的可重用代码块. 一个函数包括这几部分: 函数头 函数头包括函数访问修饰符,函数返回值类型, 函数名, ...

  6. Java基础学习中一些词语和语句的使用

    在Java基础学习中,我们刚接触Java会遇到一些词和语句的使用不清的情况,不能很清楚的理解它的运行效果会是怎么样的,如:break,continue在程序中运行效果及跳转位置, 1.先来看看brea ...

  7. Java基础 之 System.getProperty()方法

    Java基础 之 System.getProperty()方法大全 public static void main(String[] args) { System.out.println(" ...

  8. Java中hashCode()方法以及HashMap()中hash()方法

    Java的Object类中有一个hashCode()方法: public final native Class<?> getClass(); public native int hashC ...

  9. 黑马程序员----java基础笔记中(毕向东)

    <p>------<a href="http://www.itheima.com" target="blank">Java培训.Andr ...

随机推荐

  1. CentOS 5.9裸机编译安装搭建LAMP

    Linux系统:CentOS 5.9,查看CentOS版本,命令如下: [root@localhost /]# cat /etc/redhat-release CentOS release 5.9 ( ...

  2. LoadRunner(一)——性能测试基础及性能指标概述

    参考学习感谢:<精通软件性能测试与LoadRunner实战> 一.典型的性能测试场景 某个产品要发布了,需要对全市的用户做集中培训.通常在进行培训的时候,老师讲解完成一个业务以后,被培训用 ...

  3. 什么是URL,URL格式

    什么是URL: 互联网里有很多的网页,大家都需要能够互相访问,就比如在一栋大楼里,有很多的房间,不同房间里的人都想能去任意的其他房间里去,那怎么能够达到这样的想法呢? 很简单,每个房间都挂一个门牌号就 ...

  4. C# INI文件读写类

    public class Ini { // 声明INI文件的写操作函数 WritePrivateProfileString() [System.Runtime.InteropServices.DllI ...

  5. 通用类 对象和XML互转

    public class XMLHealper { /// <summary> /// 将自定义对象序列化为XML字符串 /// </summary> /// <para ...

  6. 在 Android 的文字编辑控件 (TEdit) 中, 如何按下 Enter 就隐藏虚拟键盘

    在 Windows 的应用中,我们常常为了让使用者能够快速输入,在Edit元件中的onKeyUp或者 onKeyDown 事件中主动侦测使用者输入的字元是否有换行符号 (Enter),当使用者按下了E ...

  7. 【从零开始搭建自己的.NET Core Api框架】(六)泛型仓储的作用

    系列目录 一.  创建项目并集成swagger 1.1 创建 1.2 完善 二. 搭建项目整体架构 三. 集成轻量级ORM框架——SqlSugar 3.1 搭建环境 3.2 实战篇:利用SqlSuga ...

  8. .Net Core中Dapper的使用详解

    Dapper 是一个轻量级ORM框架,在项目中如果对性能比较看中,Dapper是一个不错的选择.接下来我们就来看看如何在项目中使用Dapper. 1.安装Dapper 这里直接使用Nuget安装.   ...

  9. [Swift]LeetCode275. H指数 II | H-Index II

    Given an array of citations sorted in ascending order (each citation is a non-negative integer) of a ...

  10. [Swift]LeetCode437. 路径总和 III | Path Sum III

    You are given a binary tree in which each node contains an integer value. Find the number of paths t ...