new HashMap(int) 和 guava Maps.newHashMapWithExpectedSize(int) 的区别

Difference between new HashMap(int) and guava Maps.newHashMapWithExpectedSize(int)

在 Java 中,您可以创建一个新的 HashMap 来容纳特定数量的项目,如下所示:

Map m = new HashMap(100);

Guava 提供了一个 Maps.newHashMapWithExpectedSize(int) 方法,我希望可以简单地调用 HashMap(int)。但它不这样做,而是计算自己的容量并使用它。

为什么 newHashMapWithExpectedSize 做自己的事情,为什么我要使用它而不是直接调用 new HashMap(int)

你读过方法的Javadoc了吗?

Creates a HashMap instance, with a high enough "initial capacity" that it should hold expectedSize elements without growth.

请注意,new HashMap(int) 构造函数的 "initial size" 参数指定了存储条目的 散列 table 的初始大小,即基本上是您不必关心的实现细节。散列 table 将在超过地图的 负载因子 (默认为 0.75)时调整大小,这意味着如果您指定初始容量为 16,然后将 16 个条目添加到地图,哈希 table 几乎肯定会调整大小。

使用 Guava 的方法,如果您将 预期大小 指定为 16,然后添加 16 个条目,则散列 table 应该 而不是 调整大小。

Guava 只是将传递的大小乘以 2(以一种安全的方式)并调用常规的 hashmap 构造函数。这使得它更稀疏,因此散列时冲突更少。

关于容量计算的 javadoc 提到它计算了一个容量值,因此 hashmap 的满度在 25% 到 50% 之间,这与触发调整大小的阈值相差甚远。

标准库将预期大小四舍五入为最接近的 2 的幂并将其分配为大小,然后将调整大小的阈值设置为 75%。如果我们随机询问大小,标准库会在 50% 的情况下调整大小。

如果避免阈值是唯一的考虑因素,乘以 1.34 就足以 space 避免在用预期的元素大小填充时调整大小。

这看起来像是典型的速度与 space 的权衡,Google 工程师更狂热,而 Sun/Oracle 工程师更 space 疯狂。

HashMap构造函数的参数是映射的容量,即桶的数量。

因此,如果您传递 10 作为参数,并在映射中存储 8 个键,将达到重新哈希阈值(默认为 75%)并且映射将重新哈希。

另一方面,传递给 newHashMapWithExpectedSize() 的参数是地图的预期大小。因此,如果您传递 10,Guava 将创建一个具有足够桶的映射,以确保在插入 10 个元素时该映射不会重新散列:至少 14 个桶。