面试题答案
一键面试1. 扩容降低哈希冲突原理
在Java的HashMap
中,通过扩容来降低哈希冲突影响。HashMap
有两个重要参数:容量(capacity)和负载因子(load factor)。当HashMap
中的元素数量(size)达到容量与负载因子的乘积(即size >= capacity * load factor
)时,就会触发扩容。扩容后,新的容量是原来容量的两倍。
扩容类似于再哈希法,因为扩容后重新计算元素在新数组中的位置,元素的哈希值不变,但在新的更大的数组中,通过重新计算索引,使得哈希冲突的概率降低。例如,原本冲突在同一个桶(bucket)中的元素,扩容后可能会分散到不同的桶中。
2. 扩容过程中数据迁移细节
- 创建新数组:扩容时,会创建一个新的
Node
数组,其容量为原数组容量的两倍。 - 重新计算索引:遍历原数组中的每个桶,对于每个桶中的链表(或红黑树,JDK 8+),会重新计算每个元素在新数组中的索引位置。在JDK 8之前,是通过
(n - 1) & hash
(n
为新数组容量)来计算索引;JDK 8及之后,为了优化计算过程,对于2的幂次方容量的数组,当(hash & oldCap) == 0
时,新的索引位置与旧索引位置相同;当(hash & oldCap) != 0
时,新的索引位置为旧索引位置加上旧数组容量。 - 迁移元素:将原数组中的元素迁移到新数组对应的位置。如果原桶中是链表结构,在JDK 8之前,会直接将链表中的元素依次重新计算索引并插入到新数组对应位置;JDK 8之后,会将链表拆分成两条链表,一条是索引不变的链表,另一条是索引需要加上旧数组容量的链表,然后分别插入到新数组对应的位置。如果原桶中是红黑树结构,也会类似地重新计算索引并迁移元素,当迁移后节点数量小于等于6时,会将红黑树转换回链表。
3. 扩容过程中哈希冲突处理细节
扩容过程中,通过重新计算索引,原本冲突的元素可能会被分散到不同的桶中,从而减少哈希冲突。在数据迁移时,如果新计算的索引位置已经有元素(发生冲突),在链表结构下,新元素会被插入到链表头部(JDK 8之前)或尾部(JDK 8及之后);在红黑树结构下,会按照红黑树的插入规则插入元素,以维护红黑树的平衡性质。