HashMap 的长度(即容量)设计为 2 的幂次方,主要有以下几个原因:
-
哈希码的重新分布:
HashMap使用哈希码来确定键值对在数组中的位置。如果容量是 2 的幂次方,可以通过位运算(如&操作)来快速计算数组索引,这比使用取模运算(%)更高效。- 例如,假设容量为 16(2 的 4 次方),那么索引可以通过
hash & (capacity - 1)来计算,这里的capacity - 1是 15,即二进制的1111。这样可以确保哈希码的低位被充分利用,从而更好地分布键值对。
-
减少哈希冲突:
- 当容量是 2 的幂次方时,哈希码的低位对索引的影响更大,这有助于减少哈希冲突。如果容量不是 2 的幂次方,可能会导致某些哈希码的高位被忽略,从而增加哈希冲突的概率。
-
简化扩容操作:
HashMap在需要扩容时,会将容量扩大到原来的两倍。如果初始容量是 2 的幂次方,扩容后的容量仍然是 2 的幂次方,这使得扩容操作更加简单和高效。- 扩容时,新的索引可以通过
newHash & (newCapacity - 1)计算,其中newCapacity是原容量的两倍。由于newCapacity - 1仍然是一个全 1 的二进制数,因此位运算仍然非常高效。
示例代码
public class HashMapCapacityExample {
public static void main(String[] args) {
// 创建一个初始容量为 16 的 HashMap
HashMap<String, String> hashMap = new HashMap<>(16);
// 添加一些键值对
hashMap.put("key1", "value1");
hashMap.put("key2", "value2");
hashMap.put("key3", "value3");
// 输出 HashMap 的当前容量
System.out.println("Initial capacity: " + hashMap.size());
// 触发扩容
for (int i = 4; i <= 20; i++) {
hashMap.put("key" + i, "value" + i);
}
// 输出扩容后的容量
System.out.println("Capacity after expansion: " + ((HashMap<String, String>) hashMap).capacity());
}
}
在这个示例中,HashMap 的初始容量是 16,当添加的键值对数量超过容量时,HashMap 会自动扩容。扩容后的容量仍然是 2 的幂次方,这确保了高效的哈希码重新分布和索引计算。