无参构造
public HashMap(){
//默认构造函数,赋值加载因子为默认的0.75
this.loadFactor = DEFAULT_LOAD_FACTOR;
}
这个是默认的构造函数,使用无参构造时,初始化加载因子。其他常量在put第一条数据的时候进行初始化。
有参构造
指定容量的构造
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 (initialCapacity <= 0 && Float.isNaN(loadFactor)){
throw new IllegalArgumentException("Illegal load factor:" + loadFactor);
}
this.loadFactor = loadFactor;
//根据期望值initialCapacity,返回2的n次方形式的哈希桶实际容量length,返回值一般会大于等于期望值
this.threshold = tableSizeFor(initialCapacity);
}
双参构造可以用来指定期望容量与加载因子,之所以叫期望容量是因为hashMap会对这个值进行处理,获取大于这个值且最接近的2的n次方的数。下面是tableSizeFor方法以及说明
private 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);
}
这里涉及二进制的运算,假设cap的值是100,则n的值是99,对应的二进制为:1100011
下面开始进行运算,>>>运算符的含义是无符号右移,忽略符号位左侧补0
n |= n >>> 1; n的值二进制表达为1110011
n |= n >>> 2; n的值二进制表达为1111111
n |= n >>> 4; n的值二进制表达为1111111
n |= n >>> 8; n的值二进制表达为1111111
n |= n >>> 16; n的值二进制表达为1111111
然后对其进行边界修正最后返回。
可以看到最终期望得到的值是1111111,转换为10进制是127,加1返回即2^7,为什么非要取2的n次方呢?这个在下面扩容的时候再讲解。
根据已有map生成hashMap的构造
public HashMap(Map<? extends K,? extends V> m ){
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m,false);
}
首先指定加载因子为默认加载因子,随后调用putMapEntries方法进行节点复制。这个方法在添加元素中会单独讲解。