HashMap在JDK1.8如何确定初始化容量

大家都知道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右移一位之后做位或操作:
在这里插入图片描述
得到的二进制是110011,转化为10进制是51;

接下来n |= n >>> 2;
在这里插入图片描述
得到的二进制是111111,转化为10进制是63;

接下来是 n |= n >>> 4;
在这里插入图片描述
得到的二进制也是111111,转化为10进制是63;

接下来是 n |= n >>> 8;
在这里插入图片描述
得到的二进制也是111111,转化为10进制是63;

接下来是 n |= n >>> 16;
在这里插入图片描述
得到的二进制也是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的数~

猜你喜欢

转载自blog.csdn.net/insomsia/article/details/88536769