7. Java 中 HashMap 的扩容机制是怎样的?
大约 3 分钟
HashMap
是基于哈希表的数据结构,其容量是动态调整的。当存储的元素数量增加时,为了保持较好的性能,HashMap
需要进行扩容。HashMap
的扩容机制是为了减少哈希碰撞,提高查询效率。
1. 初始容量和负载因子
- 初始容量:
HashMap
的初始容量是指哈希表创建时桶(bucket)数组的大小,默认初始容量为16
。 - 负载因子:
HashMap
的负载因子(load factor)用于衡量HashMap
的容量增长阈值。默认负载因子是0.75
,这意味着当HashMap
中的元素数量达到当前容量的75%
时,触发扩容。
2. 扩容的触发条件
- 当
HashMap
中存储的元素数量达到“当前容量 * 负载因子”的阈值时,HashMap
会触发扩容操作。默认情况下,当元素数量达到16 * 0.75 = 12
时,就会进行扩容。
3. 扩容的过程
- 容量翻倍:当
HashMap
触发扩容时,桶数组的容量会翻倍(新容量 = 旧容量 * 2)。 - 重新哈希(rehash):扩容时,
HashMap
会将原有的所有键值对重新计算哈希码,并将它们重新分配到新的桶数组中。由于容量翻倍,哈希码的计算方式会发生变化,原本冲突在同一个桶中的元素,可能会被分配到不同的桶中,减少哈希冲突。
4. 示例代码:扩容的触发
以下代码演示了HashMap
如何在元素数量达到一定程度时触发扩容:
import java.util.HashMap;
public class HashMapResizingExample {
public static void main(String[] args) {
// 创建一个HashMap
HashMap<Integer, String> map = new HashMap<>();
// 插入13个元素,默认容量为16,负载因子为0.75
for (int i = 0; i < 13; i++) {
map.put(i, "Value" + i);
}
// 打印HashMap的大小
System.out.println("HashMap size: " + map.size());
// 插入第14个元素时,触发扩容
map.put(13, "Value13");
// 再次打印HashMap的大小
System.out.println("HashMap size after rehashing: " + map.size());
}
}
5. 扩容的影响
- 性能开销:扩容是一项开销较大的操作,因为它涉及到分配新的数组并将所有元素重新哈希。频繁的扩容操作会影响性能,因此在创建
HashMap
时,建议预估好元素数量并设置合适的初始容量,以减少扩容次数。 - 线程安全:
HashMap
本身不是线程安全的,在多线程环境下如果出现扩容,可能导致数据丢失或死循环。因此,在并发环境中使用HashMap
时,推荐使用线程安全的替代品如ConcurrentHashMap
。
总结
- 触发条件:当
HashMap
中存储的元素数量达到当前容量与负载因子的乘积时,触发扩容。 - 扩容过程:
HashMap
的容量翻倍,所有元素重新计算哈希值并重新分配到新的桶数组中。 - 性能影响:频繁扩容可能带来性能问题,建议合理设置初始容量来减少扩容次数。
理解HashMap
的扩容机制可以帮助你在使用它时做出更优化的设计和决策,尤其是在处理大量数据时。