Java 中的 HashMap 在插入元素过程中,如果现有桶的数量不足以容纳更多的键值对时,会触发扩容操作。HashMap 的扩容机制主要包含以下几个要点:
-
初始容量与负载因子:
- 初始容量:HashMap 初始化时可以指定容量大小,默认容量为 16。
- 负载因子(load factor):它是衡量 HashMap 容量是否需要扩容的一个阈值,默认值为 0.75。当 HashMap 中的元素数量超过容量与负载因子的乘积时(即
size > capacity * loadFactor
),就会触发扩容。
-
扩容过程:
- 扩容并不是简单地增加桶的数量,而是将当前桶数组的容量翻倍,例如从 16 扩容到 32,再从 32 扩容到 64 等。
- 扩容后,所有的键值对都会重新进行哈希计算,并根据新的容量分布到新的桶位置上。
- 这个过程中涉及到了大量元素的迁移,因此是比较耗时的操作。为了减少扩容频率和性能损失,通常在初始化时合理预估未来可能存储的数据量,以设置合适的初始容量。
-
Rehashing:
- 当HashMap进行扩容时,每个键值对都需要重新计算其在新表中的索引位置,这个过程叫做rehashing。
- HashMap使用了链地址法来处理哈希冲突,即同一个桶中可能会有多个元素通过链表或树形结构链接在一起。在扩容时,这些链接的元素也会随着桶的位置变化而移动。
-
扩容后的索引计算:
- 在扩容前,键值对存储的位置是通过哈希函数计算并取模得到的(
index = hash(key) & (table.length - 1)
)。 - 扩容后,新的容量是原来容量的两倍,所以对于已存在的键值对,它们在新表中的位置可能是原索引或者原索引 + 原容量,这取决于扩容后哈希值高位的变化。
- 在扩容前,键值对存储的位置是通过哈希函数计算并取模得到的(
-
扩容优化:
- 自JDK1.8开始,为了进一步优化性能,当桶内元素过多时(默认超过8个元素),该桶会被转换为红黑树结构(TreeMap实现),这样能有效降低搜索、插入和删除的时间复杂度。而在扩容后,如果桶内的元素数量小于6,则又会变回链表结构。
综上所述,HashMap的扩容机制保证了容器能够自动调整自身大小以应对数据增长,同时通过rehashing和树化等手段尽量保持高效的操作性能。