HashMap 的长度为什么是2的幂次方

385 阅读2分钟

HashMap 的长度(即容量)设计为 2 的幂次方,主要有以下几个原因:

  1. 哈希码的重新分布

    • HashMap 使用哈希码来确定键值对在数组中的位置。如果容量是 2 的幂次方,可以通过位运算(如 & 操作)来快速计算数组索引,这比使用取模运算(%)更高效。
    • 例如,假设容量为 16(2 的 4 次方),那么索引可以通过 hash & (capacity - 1) 来计算,这里的 capacity - 1 是 15,即二进制的 1111。这样可以确保哈希码的低位被充分利用,从而更好地分布键值对。
  2. 减少哈希冲突

    • 当容量是 2 的幂次方时,哈希码的低位对索引的影响更大,这有助于减少哈希冲突。如果容量不是 2 的幂次方,可能会导致某些哈希码的高位被忽略,从而增加哈希冲突的概率。
  3. 简化扩容操作

    • 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 的幂次方,这确保了高效的哈希码重新分布和索引计算。