当 HashMap 的容量达到负载因子设定的阈值时,会发生什么?扩容的过程是怎样的?
达到负载因子设定阈值时的情况
HashMap 有两个重要参数:初始容量(默认 16)和负载因子(默认 0.75)。当 HashMap 中存储的键值对数量超过了容量与负载因子的乘积(即阈值,threshold = capacity * loadFactor),就会触发扩容操作。
触发扩容主要是为了保证 HashMap 的性能。随着元素不断增加,哈希冲突发生的概率会上升,链表或红黑树会变长,查找、插入和删除操作的时间复杂度会变高。通过扩容可以增加桶的数量,减少哈希冲突,使元素分布更均匀,维持操作的时间复杂度接近 O (1)。
扩容的过程
HashMap 的扩容过程主要分为以下几个步骤:
创建新数组:容量变为原来的 2 倍。例如,原本容量为 16,扩容后变为 32。
重新计算阈值:根据新的容量和负载因子重新计算阈值。
元素迁移:遍历原数组的每个桶,将其中的元素重新分配到新数组中。这里又分为以下几种情况:
单个节点:如果桶中只有一个节点,直接根据新的哈希值和新数组长度计算新的位置并放入。
链表节点:如果桶中是链表,遍历链表,根据元素的哈希值和新数组长度计算每个元素在新数组中的位置,将链表拆分为两个链表,分别放入新数组的不同位置。
树节点:如果桶中是红黑树,将树拆分为两个树或者链表(如果拆分后节点数量小于 6,会将红黑树转换回链表),再放入新数组的不同位置。
以下是简化的 Java 代码示例,展示了 HashMap 扩容的大致逻辑:
import java.util.HashMap;
public class HashMapResizeExample {
public static void main(String[] args) {
// 创建一个初始容量为 4 的 HashMap
HashMap<Integer, String> map = new HashMap<>(4);
// 不断添加元素,触发扩容
for (int i = 0; i < 10; i++) {
map.put(i, "Value " + i);
System.out.println("Size: " + map.size() + ", Capacity: " + tableSizeFor(map.size()));
}
}
// 模拟获取 HashMap 的容量
static final int tableSizeFor(int cap) {
int n = -1 >>> Integer.numberOfLeadingZeros(cap - 1);
return (n < 0) ? 1 : (n >= 1 << 30) ? 1 << 30 : n + 1;
}
}

浙公网安备 33010602011771号