为啥Java中BitSet的内部数据存储为long[]而不是Java中的int[]?

Posted

技术标签:

【中文标题】为啥Java中BitSet的内部数据存储为long[]而不是Java中的int[]?【英文标题】:Why is the internal data of BitSet in java stored as long[] instead of int[] in Java?为什么Java中BitSet的内部数据存储为long[]而不是Java中的int[]? 【发布时间】:2015-11-13 15:23:53 【问题描述】:

在java中,BitSet的内部数据存储为long[]而不是int[],我想知道为什么?这是jdk中的代码:

 /**
 * The internal field corresponding to the serialField "bits".
 */
 private long[] words;

如果一切都与性能有关,我想知道为什么 long[] 存储会获得更好的性能。

【问题讨论】:

我想知道为什么不呢?将其存储为 int[] 究竟有什么更好的方法? 如果我不得不猜测我会说这可能与现在大多数人都在 64 位机器/操作系统上的事实有关,因此长期操作往往得到更好的支持/更快。我真的不认同 Santi 的论点。 @devouredelysium 我认为“大多数人都使用 64 位”确实是一个非常大胆的声明。除了轶事之外,你有任何证据支持这一点吗?鉴于 Java 被设计为在无数平台(包括许多嵌入式系统)上运行,我真的认为这不是设计决策背后的原因。 @daiscog:但他们选择了多头而不是整数。如果你看一下 BitSet 的源代码,它清楚地表明:“BitSets 被打包成“单词”的数组。 @devouredelysium 对不起,我想你可能误解了我的意思。我知道他们选择长整数而不是整数,但我是说我不相信你给出的原因就是他们这样做的原因。 【参考方案1】:

在 64 位机器上,对单个 long 值执行按位运算比对两个 int 值执行相同操作的性能要高得多,因为硬件直接支持 64 位值。在 32 位机器上,差异可能不是很显着。

【讨论】:

在 32 位机器上,使用 int[] 还是 long[] 性能会更好吗? @displayName,您可以编写自己的基于 int[] 的BitSet 并自行测试:-) 另请参阅@Holger 答案。 @displayName 我会说 int[] 因为它是原生的 :D 但 Tagir 是对的......我们应该实施并看看 @TagirValeev:您对我的评论的回应是最好的。 :) 我还是问了,因为我想你可能已经知道了。在过去的 ~ 2 年里,我一直在使用 C#。在我的笔记本电脑上启动 Eclipse 需要大量的“热身”。 :D @displayName:请记住,即使是 32 位 CPU,也可能具有 64 位内存总线以及一些适当的 64 位操作。这与一般算术无关,而只是某些位操作。此外,JVM 可能了解标准的BitSet 类,如果它们“不够原生”,则替换某些操作。更大的障碍是找到具有 true 32 位架构的合适测试机器……【参考方案2】:

查询或操作单个位时,没有显着差异。您必须计算单词索引并读取该单词,并且在更新的情况下,操作该单词的一位并将其写回。 int[]long[] 都是一样的。

有人可能会争辩说,如果你有一个真正的 32 位内存总线,使用 long 而不是 int 可能会增加必须为单个位操作传输的内存量,但由于 Java 是设计的在上世纪九十年代,设计师们认为这不再是一个问题。

另一方面,在一次处理多个位时,您将获得巨大的胜利。当您对整个BitSet 执行andorxor 之类的操作时,您可以在使用long 数组时一次对整个字执行操作,读取64 位。

类似地,searching for the next set bit 时,如果该位不在起始位置的字内,则后续字首先针对零进行测试,这是一种内在操作,即使对于大多数 32 位 CPU,您也可以跳过 64 个零位,而第一个非零字肯定会包含下一个设置位,因此整个迭代只需要一次位提取操作。

批量操作的这些好处将超过任何与单个位相关的缺点,如果有的话。如前所述,当今的大多数 CPU 都能够直接对 64 位字执行所有操作。

【讨论】:

由于BitSet是一种数据结构,所以在谈到性能时,我们应该考虑它支持的操作。你是第一个以这种方式分析表演的人。干得好!【参考方案3】:

基于对来源here 的粗略阅读。似乎,主要原因纯粹是为了性能。这是从源中检索到的评论。

BitSet 被打包成“单词”数组。目前一个词是 一个long,由64位组成,需要6个地址位。 字长的选择完全取决于性能问题。

【讨论】:

如果只看性能,我想知道为什么 long[] 存储会获得更好的性能。 类将其容量调整 64 比 32 更快。【参考方案4】:

我可能错了,但是使用 long[] 时 bitSet 的基数比使用 int[] 时大得多。因为它们的最大数组大小非常相似(但仅限于堆大小)。

【讨论】:

这表面上是有道理的;数组的最大元素意味着 long 数组为您提供更多可能的位。但是,我不认为想要拥有这么多标志的人的用例是一个足够现实的担忧,这才是真正的原因。 BitSet 的方法使用int 索引参数和返回值,因此由于 API 限制为 2³¹ 位。因此,其内部阵列所施加的理论限制是高出 32 倍还是高出 64 倍并不重要。即使是 byte 数组也可以存储比 API 支持的更多的位。 是的,bitSet 内部数组是用整数索引的,但值很长,所以我们比使用 int 时获得更多可能的位。 我说的不是数组索引,而是BitSetAPI。尝试set 一个大于 2³¹ 的数字——这是不可能的,因为没有提供该操作的方法。同样,size() 返回一个int。因此,在内部使用 long[] 而不是 int[] 不会获得更多位。使用当前的 API,它仍然最多 2³¹。【参考方案5】:

肯定是一个优化问题:单个long 值最多可存储64 位,而int 仅存储32 位。因此,任何小于64 的用户长度都只需要数组中的一个条目。如果它是int 的数组,则需要两个条目,这样维护起来更慢且更重。

【讨论】:

为什么它更慢,为什么更难维护? 显然,在一个数组中存储两个项目比只存储一个项目需要两倍的时间。 不一定。这在很大程度上取决于您的硬件。 我不是指内联存储值,而是在循环中,这是像 BitSet 这样的通用解决方案中的场景:for (int i=0;i<n;i++) array[i]=0; 需要更多时间与 n== 2 比 n==1。 对我来说就是这样。如果我正在设计课程,我会出于这个原因选择一个 long 数组(除了我不同意的“更重的维护”)。

以上是关于为啥Java中BitSet的内部数据存储为long[]而不是Java中的int[]?的主要内容,如果未能解决你的问题,请参考以下文章

为啥带有 unsigned long long 参数的 std::bitset 构造函数未标记为显式?

BitSet 的使用

BitSet 的 size() 方法的原因是啥?

Java Bitset

Java:计算 java.util.BitSet 中设置的位数

Bitset