在 Java 中,您可以创建一个新的
HashMap
来保存特定数量的项目,如下所示:
Map m = new HashMap(100);
Guava 提供了一个
Maps.newHashMapWithExpectedSize(int)
方法,我希望它可以简单地调用HashMap(int)
。但它并没有这样做,而是计算自己的容量并使用它。
为什么
newHashMapWithExpectedSize
做自己的事情,为什么我要使用它而不是直接调用 new HashMap(int)
?
你读过方法的Javadoc了吗?
创建一个
实例,具有足够高的“初始容量”,它应该容纳HashMap
元素而不增长。expectedSize
请注意,
new HashMap(int)
构造函数的“初始大小”参数指定存储条目的哈希表 的初始大小,这基本上是您不必关心的实现细节。当哈希表超过地图的 load factor(默认为 0.75)时,它会调整大小,这意味着如果您指定初始容量为 16,然后向地图添加 16 个条目,则哈希表几乎肯定会调整大小。
使用 Guava 的方法,如果您指定 expected size 为 16,然后添加 16 个条目,哈希表应该 not 调整大小。
HashMap 的构造函数参数是映射的容量,即桶的数量。
因此,如果您将 10 作为参数传递,并在映射中存储 8 个键,将达到重新哈希阈值(默认为 75%)并且地图将重新哈希。
另一方面,传递给 newHashMapWithExpectedSize() 的参数是地图的预期大小。因此,如果您传递 10,Guava 将创建一个具有足够桶的地图,以确保在插入 10 个元素时地图不会重新散列:至少 14 个桶。
Guava 只是将传递的大小乘以 2(以安全的方式)并调用常规的 hashmap 构造函数。这使得它更稀疏,因此散列时冲突更少。
关于容量计算的 javadoc 提到它计算容量值,使 hashmap 充满 25% 到 50%,这远离会触发调整大小的阈值。
标准库将预期大小四舍五入为最接近的 2 的幂并将其分配为大小,然后将调整大小的阈值设置为 75%。如果我们随机询问大小,标准库会在 50% 的情况下调整大小。
如果避免阈值是唯一的考虑因素,乘以 1.34 就足以有足够的空间来避免在用预期的元素大小填充它时调整大小。
这看起来像是典型的速度与空间的权衡,谷歌的工程师更喜欢速度,而 Sun/Oracle 的工程师更喜欢太空。