【问题标题】:Fastest Java HashSet<Integer> library [closed]最快的 Java HashSet<Integer> 库 [关闭]
【发布时间】:2012-08-06 21:55:47
【问题描述】:

除了this quite old post,我还需要一些可以使用原语并为包含大量HashSets 或Integers 的应用程序提供加速的东西:

Set<Integer> set = new HashSet<Integer>();

所以人们会提到像 Guava、Javalution、Trove 这样的库,但是在基准测试和性能结果方面并没有完美的比较,或者至少是来自良好经验的好答案。我看到很多人推荐Trove的TIntHashSet,但也有人说它不是那么好;有人说 Guava 超酷且易于管理,但我不需要美观和可维护性,只需要时间执行,所以 Python 风格的 Guava 就回家了 :) Javalution?我访问过该网站,对我来说似乎太旧了,因此很古怪。

库应该提供最佳的可实现时间,内存无关紧要。

查看“Thinking in Java”,有一个想法是使用int[] 作为键创建自定义HashMap。所以我希望看到与HashSet 类似的东西,或者直接下载并使用一个很棒的库。

EDIT(回应下面的cmets) 所以在我的项目中,我从大约 50 个 HashSet&lt;Integer&gt; 集合开始,然后我调用一个函数大约 1000 次,内部创建多达 10 个 HashSet&lt;Integer&gt; 集合。如果我更改初始参数,数字可能会呈指数增长。我只对这些集合使用add()contains()clear() 方法,这就是选择它们的原因。

现在我将找到一个实现HashSet 或类似功能的库,但由于自动装箱Integer 开销以及可能我不知道的其他东西,它会更快地完成。事实上,当我的数据进来时,我使用整数并将它们存储在那些HashSets 中。

【问题讨论】:

  • 我们对您的项目了解不足,无法为您拨打这个电话。大多数项目都可以毫无问题地使用Set&lt;Integer&gt;。我建议你试试你提到的库并衡量你的表现。
  • 如果您要映射的范围很大,我建议不要使用位设置。正如@JimGarrison 所说,[Integer.MIN_VALUE, Integer.MAX_VALUE] 将占用(根据WolframAlpha)512 MiB 使用位设置。
  • 如果您正在研究为整数选择哈希表的特定底层实现,请知道,由于元素很小并​​且可以放入缓存行,您可以查看 open -寻址而不是单独的链接。然而,在这个阶段这可能是premature optimization
  • 整个问题似乎是过早的优化。
  • 不能自己写HashSet吗?这并不难。

标签: java performance hashset


【解决方案1】:

看看High Performance Primitive Collections for Java (HPPC)。它是 trove 的替代品,成熟且为提高效率而精心设计。请参阅IntOpenHashSet 的 JavaDoc。

【讨论】:

    【解决方案2】:

    Trove 是一个绝佳的选择。

    它比通用集合快得多的原因是内存使用。

    java.util.HashSet&lt;Integer&gt; 在内部使用java.util.HashMap&lt;Integer, Integer&gt;。在HashMap 中,每个对象都包含在Entry&lt;Integer, Integer&gt; 中。这些对象占用Entry 的估计 24 个字节 + 实际整数的 16 个字节 + 实际哈希表中的 4 个字节。这产生了 44 个字节,而 Trove 中的 4 个字节,高达 11 倍的内存开销(请注意,主表中未占用的整体将在实践中产生较小的差异)。

    另见这些实验:

    http://www.takipiblog.com/2014/01/23/java-scala-guava-and-trove-collections-how-much-can-they-hold/

    【讨论】:

      【解决方案3】:

      您是否尝试在创建 HashSet 时使用初始容量和负载因子参数?

      HashSet doc

      初始容量,你可能会想,是指空的哈希集在创建时有多大,而负载因子是一个阈值,它决定了何时增长哈希表。通常情况下,您希望将已用桶与总桶的比例保持在三分之二以下,这被认为是在哈希表中实现良好稳定性能的最佳比例。

      Dynamic rezing of a hash table

      因此,基本上,尝试设置一个适合您需要的初始容量(以避免在哈希表增长时重新创建和重新分配值),以及摆弄负载因子直到找到最佳位置.

      对于您的特定数据分布和设置/获取值,较低的负载因子可能会有所帮助(几乎没有更高的负载因子,但您的里程可能会有所不同)。

      【讨论】:

      • 是的,我确实尝试过,但不幸的是,当我知道HashSet 的大小并告诉它new HashSet(n / 2) 时,它的性能比没有该容量时要差...很奇怪,我猜它创建大数组并在查找时花费大量时间(可能在包含期间),好吧,至少在 Thinking in Java 中的 Bruce Eckel 说过类似的话。无论如何,谢谢你的回答,我有点接受它:)
      • 你为什么要做n / 2?如果您知道您将恰好有 n 个元素,请使用 n。在最佳情况下,每个桶将只有一个元素,但如果碰巧发生冲突(如果您的数据集不是很大),只要您不重新计算和重新分配哈希表,性能就不会受到太大影响(即插入超过 n 个元素,在您的情况下为 n/2)。根据哈希表文档 (docs.oracle.com/javase/6/docs/api/java/util/Hashtable.html)
      • " "初始容量控制了浪费空间和需要重新哈希操作之间的权衡,这些操作非常耗时。如果初始容量大于 Hashtable 将包含的最大条目数除以其负载因子,则不会发生重新哈希操作。但是,将初始容量设置得太高会浪费空间。”因此,如果您只保存 int,那么浪费空间的问题应该不是问题。尝试将其设置为至少 n,或者甚至更好,尝试 n+1 :
      • 再次从文档中:“如果要将许多条目放入哈希表中,则创建具有足够大容量的条目可能比让它根据需要执行自动重新散列更有效地插入条目扩大表格”
      • 超过 n,尝试实际执行 capacity=x where x= 4/3 n(即 n * 1.33333),这样如果负载因子是默认值(0.75),您只需要重新散列如果您达到 3/4x,则该表将是 n,因此不会发生重新散列(并考虑舍入错误,请尝试 x+1)
      猜你喜欢
      • 1970-01-01
      • 2012-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多