【问题标题】:Difference between new HashMap(int) and guava Maps.newHashMapWithExpectedSize(int)new HashMap(int) 和 guava Maps.newHashMapWithExpectedSize(int) 的区别
【发布时间】:2015-07-25 02:16:30
【问题描述】:

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

Map m = new HashMap(100);

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

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

【问题讨论】:

  • 我的回答涵盖了这一点,但这里的根本问题是 JDK API 很糟糕,因为它没有像您期望的那样调整 HashMap“以容纳特定数量的项目”的大小将。相反,它将其大小调整为您指定的数字的 75%。

标签: java collections guava


【解决方案1】:

你读过方法的Javadoc吗?

创建一个HashMap 实例,该实例具有足够高的“初始容量”,可以容纳expectedSize 元素而不会增长。

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

使用 Guava 的方法,如果您指定 预期大小为 16,然后添加 16 个条目,则哈希表不应调整大小。

【讨论】:

  • 不,你的例子不会这样做。标准库会将大小四舍五入到 16(2 的下一个幂),并且用 10 个项目填充它不会超过扩展大小的阈值。但是,如果您的预期大小为 13 并用 13 填充它。
  • 谢谢,已将特定示例修复为使用 16。对于任意大小,如果您指定的初始容量等于您要添加的条目数,则表格可能会调整大小通常是正确的。
【解决方案2】:

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

关于容量计算的 javadoc 提到它计算了一个容量值,以便哈希图在 25% 到 50% 之间充满,这与触发调整大小的阈值相差甚远。

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

如果避免阈值是唯一的考虑因素,那么乘以 1.34 就足以有足够的空间来避免调整大小以填充预期的元素大小。

这看起来像是典型的速度与空间权衡,Google 工程师更喜欢速度,而 Sun/Oracle 工程师更喜欢空间。

【讨论】:

  • 不。如果您调用new HashMap(n) 然后放入n 个条目,哈希表将被调整大小,因为它按照JDK 的定义不够大;番石榴只是弥补了这一点。
  • 是的,正如 Louis 所说,无论您使用哪种方法构造 Map,哈希表最终的大小都将大致相同,因此不应有任何明显的空间差异。唯一的区别是在添加所有要添加的条目之前是否必须调整哈希表的大小。
  • 是的,你是对的。它分配四舍五入到 2 的下一个幂的大小,然后设置 75% 的正常负载因子,这将在填充大小项目时触发(如果大小足够接近四舍五入的数字,或大约 50%例)。我错误地认为标准库弥补了这一点。数字 2 仍然是相当随意的,所以我仍然相信稀疏性也在那里起作用。
【解决方案3】:

HashMap构造函数参数是map的容量,即桶的个数。

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

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

【讨论】:

  • A HashMap 使用 2 的下一个幂作为其容量。因此,如果您将 initialCapacity 设置为 10,则实际容量将为 16。将其乘以 0.75 会产生总共 12 个条目而无需调整大小。
  • @Bubletan:这是真的,但是 A)它是 API 完全未指定的实现细节,并且 B)它不会改变这样一个事实,即对于 initialCapacity 的许多可能值,为了添加这么多条目,必须调整表的大小和重新散列。
  • @ColinD 同意,如果不阅读代码,就无法从 javadoc 中知道实际的初始容量。这实际上在某些地方具有误导性。
猜你喜欢
  • 2018-03-20
  • 2020-09-24
  • 2019-08-14
  • 1970-01-01
  • 1970-01-01
  • 2018-02-25
  • 1970-01-01
  • 2017-10-17
  • 2010-12-08
相关资源
最近更新 更多