1、HashMap的put过程
- 首先根据key值计算hash值,找到该元素在数组中存储的下标
- 如果数组是空的,则调用resize进行初始化
- 如果没有哈希冲突直接放在对应数组下标里
- 如果冲突了,且key已经存在,就覆盖掉value
- 如果冲突后是链表结构,就判断该链表是否大于8,如果大于8并且数组容量小于64,就进行扩容;如果链表节点数量大于8且数组的容量大于64,则将这个结构转换成红黑树;否则,链表插入键值对,若key存在,就覆盖掉value
- 如果冲突后,发现该节点是红黑树,就将这个节点挂在树上
2、HashMap的容量为什么必须是2的次方数
- 计算索引位置的公式为(n-1)& hash值,n就是hash表长度,当n为2的N次方数时,n-1的低位全是1,此时任何hash值和n-1进行&运算的结果为该hash值的低位,如果n不是2的次方数,则&操作后计算出的地址hash冲突的几率会很大,例如:当hash表长度为6时(不是2的幂次方),5按位与后为5,但7与5按位与后结果也是5,导致hash冲突(因为hash值是唯一的,所以按位与后对原hash的低位计算后不改变是最优解)
3、 HashMap1.7计算哈希码为什么要同时用到高低位?
- 如果hash表的长度较小,则让高位也参与运算无伤大雅,并不会有太大开销,因为位运算性能很高,但是,若不加入高位运算的话,则结果只取决于低位,无论高位怎么变,结果都一样,如果加入高位运算,则索引计算结果就不会仅取决于低位,进一步降低了hash冲突的发生
4、 在HashMap中同一个桶位中的元素,哈希码都是一样的吗?为什么?
- 大多数情况下是不一样的,因为hashmap中的哈希函数也叫散列函数,它的目的就是让hashmap中的对象散列分布,所以2个不同的对象产生相同的hashcode的概率是很低的
5、 在HashMap扩容时,同一个桶位中的元素会被分配到新数组中的什么位置,为什么?
-
hashMap扩容后,变为原hash表长度的两倍,即假设原表长度为:16,则扩容后的长度为32,按照索引地址计算公式(n-1)& hash,同一桶位有两个元素分别是hash值为1和hash值17,它们计算索引地址后结果一样,所以放在了一个桶位,在扩容后,hash值为1的位置不变,hash值为17的位置变为17
-
桶位中只有一个节点时,新位置就是hash &(新表长度-1),桶位中有多个节点时,判断hash&旧表长度 ==0 ,如果为true,则在新表中与旧表中的位置一样,为false时,位置为旧表位置+旧表容量
为什么红黑树和链表都是通过 e.hash & oldCap == 0 来定位在新表的索引位置?
- 假设a、b节点在同一索引位置,table容量为16,扩容后变为32,新表的n-1只比旧表在高位多了一个1,所以在计算新表索引位置时,只取决于在新表高位多出来的这一位,(低位都是一样的)且这一位的值刚好等于旧表长度(oldCap)