面试官最喜欢拷打的java集合里最爱拷打的集合,它的名字叫HashMap!!!
1.为什么HashMap初始化大小设置为16?
/**
* The default initial capacity - MUST be a power of two.
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
上面这一段代码是HashMap的默认初始化大小1<<4也就是16,定义这个静态常量的注释写的很清楚,要求一定是2的幂次。那为什么是16呢?难道不是2的整数次幂都行嘛?理论上是都行,但是如果是2,4或者8会不会有点小,添加不了多少数据就会扩容,也就是会频繁扩容,这样岂不是影响性能,那为啥不是32或者更大,那不就浪费空间了嘛,所以啊,16就作为一个非常合适的经验值成为了初始化的大小,你当然可以在定义HashMap时就传入数组大小参数从而实现自定义HashMap的初始大小,那么理所当然我们会问了:为什么要求HashMap大小一定是2的整数次幂呢?
不过还是要纠正一点在JDK1.7的时候初始容量确实是16,但是JDK1.8的时候初始化HashMap的时候并没有指定容量大小,而是在第一次执行put数据,才初始化容量。如下代码是HashMap的数组,存储的是Node<K,V>类型,并没有初始化。
/**
* The table, initialized on first use, and resized as
* necessary. When allocated, length is always a power of two.
* (We also tolerate length zero in some operations to allow
* bootstrapping mechanics that are currently not needed.)
*/
transient Node<K,V>[] table;
hashMap()方法也没有对数组大小赋值,只对加载因子进行了赋值:
/**
* Constructs an empty <tt>HashMap</tt> with the default initial capacity
* (16) and the default load factor (0.75).
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
2.为什么HashMap的数组大小要求是2的整数次幂呢?
我们都知道HashMap是由数组和链表或者数组加红黑树(JDK8)构成的,存储元素时会根据元素的hash值来决定元素的存储位置,这里的hash值计算不是简单的重写hashcode获得的,但是根本上还是对存储的元素的key的hashcode值进行了一系列操作获得的,像这样:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
通过hash()方法获得了key对应的hash值,那这里为什么选择这么个异或操作来获取hash值呢?其实也很好理解,是为了让hashcode值的每一位都参与到运算里从而减少hash碰撞,因为后面的运算只用到了后面的mouji位:
其中的如下代码就是对hash进行和数组最大序号之间的与操作,使得这个hash值能够对应到数组的其中一位里面:
tab[i = (n - 1) & hash]