1. HashMap触发扩容条件
- hashMap默认的负载因子是0.75,即如果hashmap中的元素个数超过了总容量75%,则会触发扩容
- 如果某个桶中的链表长度大于等于8了,则会判断当前的hashmap的容量是否大于64,如果小于64,则会进行扩容;如果大于64,则将链表转为红黑树。
2. HashMap求桶的位置
HashMap求桶的位置一共分为三个过程:
1)求key的hashcode
2)将hashcode的高16位和低16位进行异或操作。
至此我们完成了hash方法,求得了该元素的hash值。源码在下方
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
3)(n - 1) & hash ,将hash值与length-1进行与操作,求桶的位置
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
3. HashMap的扩容过程
无论是JDK7还是JDK8,HashMap的扩容都是每次扩容为原来的两倍,即会产生一个新的数组newtable,我们需要把原来数组中的元素全部放到新的只不过元素求桶的位置的方法不太一样。
在JDK7中就是按照我上述写的三个步骤重新对元素求桶的位置,但是第三步与的值是新的数组的长度-1,也就是newCap-1。
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;//插入新值
但是JDK8中就不是和newCap,而是直接与oldCap,也就是与旧数组的长度(oldCap)进行与操作。下面的是伪代码:
if ((e.hash & oldCap) == 0) {
newTab[j] = loHead;
}else{
newTab[j + oldCap] = hiHead;
}
与oldCap与的结果如果是0,那么就代表当前元素的桶位置不变。
如果结果为1,那么桶的位置就是原位置+原数组长度(oldCap)
注意: hashMap扩容的时候会判断当前的桶的位置有没有链表或者红黑树,如果没有链表或者红黑树,那么当前元素还是和JDK1.7中的求法一样,求新的桶的位置。如果有链表,那么链表的元素会按照我刚刚所说的求法去求新的桶的位置。如果是红黑树,则会调用split方法,将红黑树切分为两个链表,之后进行扩容操作,这部分我还不是很懂。