HashMap的扩容过程

一:扩容条件

在Java中,HashMap的扩容条件是基于当前HashMap容量(即内部数组的大小)和实际存储元素的数量。具体来说,在Java 7及以后版本中,HashMap扩容的触发条件如下:
1,装载因子阈值: 当HashMap中的元素数量(entry数量)超过当前容量与预设的负载因子(load factor)的乘积时,会触发扩容操作。默认负载因子为0.75,也就是说,当HashMap中的元素个数达到容量的75%时,就会进行扩容。

2,插入新元素时: 在执行put()操作尝试插入一个新的键值对时,如果发现现有元素数量已经达到了扩容阈值,并且确实需要新增一个元素(不是替换已存在的元素),那么也会触发扩容操作。
扩容过程涉及创建一个新的、更大容量的数组,并将原数组中的所有键值对重新计算哈希值并移动到新的数组中。这个过程也称为“rehashing”,并且在Java 8中引入了优化,链表长度大于某个阈值时会转化为红黑树,以减少搜索、插入和删除的时间复杂度。

二:举例说明:

初始容量为16,当添加第13个元素时,HashMap扩容的流程是什么样的

当初始容量为16的HashMap添加第13个元素时,由于默认负载因子是0.75,所以扩容阈值(threshold)计算公式为:capacity * loadFactor = 16 * 0.75 = 12。

因此,当添加第13个元素时,HashMap的实际存储元素数量超过了扩容阈值(即当前已存储12个元素+即将添加的第13个元素),将会触发扩容操作。

扩容流程如下

创建新的Entry数组: HashMap会创建一个新的Entry数组,其容量通常是原来容量的两倍加一,也就是newCapacity = oldCapacity << 1 = 16 * 2 = 32。

重新哈希所有元素: 遍历原HashMap中的每个Entry(键值对),使用新的容量重新计算它们的索引位置,并将这些Entry放入新的数组中。这个过程被称为“rehash”。

链表拆分或树形结构调整: 如果在扩容过程中某个桶(bucket)下形成了链表结构,那么在这个桶下的链表会根据新的索引被拆分成两个链表,分别存放在新数组的相应位置。在Java 8及以上版本中,如果链表长度超过8且HashMap允许转化为红黑树(通过treeifyThreshold控制,默认也是8),则链表会被转换为红黑树。

替换引用: 扩容完成后,HashMap会将内部引用指向新的Entry数组,旧数组将不再使用,随后垃圾回收机制会在适当的时候回收旧数组。

这样就完成了HashMap的扩容操作,之后可以继续安全地添加新的元素,而不会因为超出容量限制导致无法插入。

小提醒:
HashMap初始化之后,并没有立即分配内存空间,初始化时并没有初始化数组 table,在 put 操作时才初始化;

你可能感兴趣的:(#,集合,java,HashMap)