大家都知道HashMap是采用的懒加载机制,也就是说在你执行new HashMap()的时候,构造方法并没有在构造出HashMap实例的同时也把HashMap实例里所需的数组给初始化出来。

那么什么时候才去初始化里面的数组呢?答案只有在第一次需要用到这个数组的时候才会去初始化它,就是在你往HashMap里面put元素的时候。

而初始化数组时,它的容量是怎么确定的呢?有两种情况:
第一种是你在构造HashMap实例的时候,调用的是无参构造函数,此时默认的数组初始化长度就是16,在后续put元素初始化数组时生效。

public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

第二种情况,则是你调的是带了数组容量参数的构造函数:

public HashMap(int initialCapacity) {
            this(initialCapacity, DEFAULT_LOAD_FACTOR);
        }

或者

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;
        this.threshold = tableSizeFor(initialCapacity);
    }

当然可以看出上面那个构造方法里面执行的是this(initialCapacity, DEFAULT_LOAD_FACTOR),实际也就是下一个构造方法。

当你调用带参构造器初始化一个指定数组容量的HashMap时,构造器会根据你输入的参数重新计算得到一个HashMap初始化数组之后数组实际的长度,这个值也是会在put元素初始化数组时起作用。计算的逻辑在tableSizeFor(int cap)中:

static final int tableSizeFor(int cap) {
        int n = cap - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    }

这个方法的作用是什么呢,比如你输入的容量参数为A,返回值为B,那么A与B的关系如下:
1、B大于或等于A
2、B为最接近A的一个2的n次方
举几个栗子就是:
1、输入1,返回1(2^0)
2、输入2,返回2(2^1)
3、输入3,返回4(2^2)
4、输入4,返回4(2^2)
5、输入5,返回8(2^3)
依此类推~

那么方法体是如何实现这些逻辑的呢,从代码中可以看到大部分代码都是在做无符号右移(>>>)和位或(|),这里其实都是在做2进制的位操作,描述起来比较困难,还是来举几个栗子吧,假如你输入的参数为35:

int n = cap - 1;

那么n = 34;

那么34对应的2进制编码为:100010

n | n >>> 1等同于n = n | n >>> 1,就是34与34右移一位之后做位或操作:

java 中 hashMap 的初始化 hashmap什么时候初始化数组_ci


得到的二进制是110011,转化为10进制是51;接下来n |= n >>> 2;

java 中 hashMap 的初始化 hashmap什么时候初始化数组_数组_02


得到的二进制是111111,转化为10进制是63;接下来是 n |= n >>> 4;

java 中 hashMap 的初始化 hashmap什么时候初始化数组_ci_03


得到的二进制也是111111,转化为10进制是63;接下来是 n |= n >>> 8;

java 中 hashMap 的初始化 hashmap什么时候初始化数组_数组_04


得到的二进制也是111111,转化为10进制是63;接下来是 n |= n >>> 16;

java 中 hashMap 的初始化 hashmap什么时候初始化数组_数组_04


得到的二进制也是111111,转化为10进制是63;

最终 return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1这一行,
先判断n是否大于0:
1、不大于0返回1
2、否则再判断n是否大于定义的最大容量(1>>30):
1:若大于1>>30,则返回1>>30
2:否则返回n+1

实际最终这个方法,就是利用移位与位或运算,将n-1得到的值转成2进制之后,从1的最高位开始将低位全部转化为1,再加1之后就可以得到一个2^n的数~