为啥 Java `BitSet` 没有 `shiftLeft` 和 `shiftRight` 函数?

Posted

技术标签:

【中文标题】为啥 Java `BitSet` 没有 `shiftLeft` 和 `shiftRight` 函数?【英文标题】:Why does Java `BitSet` not have `shiftLeft` and `shiftRight` functions?为什么 Java `BitSet` 没有 `shiftLeft` 和 `shiftRight` 函数? 【发布时间】:2012-02-24 11:08:46 【问题描述】:

这些缺失有什么特别的原因吗?

它们确实存在于BigInteger,但由于BigInteger 的不可变设计模式,它们通常非常缓慢。 BitSet 更好,因为它是可变的,但我真的很想念 shift 函数(<<>>> 用于 longs)。对于BitSet,原地移位以及循环旋转也很有用。

我看到了Shifting a Java BitSet的回复(用get(off, len)换档,不过需要复制)。

不要误会我的意思。我知道在哪里报告错误。我只是想知道是否有特定的 reason 省略它们,例如一些设计模式或这样的概念。特别是因为它们包含在BigInteger 中。

【问题讨论】:

因为它是一个“集合”,而不是一个“字符串”。 @bmargulies:long 也不是字符串。然而,它有轮班操作员。而String 实际上没有。而get(i,j) 语义本质上与substring 一致,也不适用于long... 术语“集合”的意思是“一个无序的集合”。 BitSet 的任务是知道打开了 2 的哪些幂,而不是打乱它们。 @bmargulies - 尽管它的名字,BitSet 实际上被设计为一个向量(由非负整数索引的值的集合),而不是一个集合。 @Anony-Mousse:很难说“为什么”,但我能看到的一个原因如下:人们转移比特并做优化的事情,比如“打包东西”通过打包和移位位的整数/长整数通常与速度性能有关。但是speedoptimization基本上与“创建Java对象”相反:并不是说创建Java对象特别慢……而是操纵long/int 和移位位基本上尽可能接近金属......(当然这只是一个理论) 【参考方案1】:

我的猜测是这会使他们的一些代码变得更加复杂。例如,如果您将所有内容都“左移 3”,则可以有一个额外的字段 shift,即 -3(或者可能是 3,我只有 50% 的机会将其正确:-)。而且,对于 get() 和 set() 方法,如果您只是通过移位调整 bitIndex,代码应该可以工作。例如

public boolean get(int bitIndex) 
    bitIndex += shift;  // new code!!!
    if (bitIndex < 0)
        throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex);

    checkInvariants();

    int wordIndex = wordIndex(bitIndex);
    return (wordIndex < wordsInUse)
        && ((words[wordIndex] & (1L << bitIndex)) != 0);
    

但是,对于其他一些操作,例如 intersects() 和 or(),代码会开始变得非常混乱。现在 or() 方法的核心非常简单快速:

 // Perform logical OR on words in common
   for (int i = 0; i < wordsInCommon; i++)
      words[i] |= set.words[i];

   // Copy any remaining words
   if (wordsInCommon < set.wordsInUse)
     System.arraycopy(set.words, wordsInCommon,
                  words, wordsInCommon,
              wordsInUse - wordsInCommon);

如果两个 BitSet 都有可能的转变,这将很快变得混乱。他们可能认为,如果你真的想转移,你应该使用 get 和 copy。

让我吃惊的一件事——在 get() 中,他们不做 1L &lt;&lt; bitIndex&amp;31。显然

【讨论】:

是的,我考虑过这样做。但我真的需要orxor 才能工作。 Java 确实有代码可以在BigInteger 中对int[] 进行转换,并且他们几乎可以将其复制到BitSet 以获取long[]。并没有太大的不同。【参考方案2】:

从概念上讲,BitSet 通常/经常用于跟踪很多设置,这样集合中的每个位都有特定的含义。因此,在这种情况下,移位操作毫无意义。

您显然已经找到了 BitSet 的另一个有用用途,但它超出了 BitSet 可能设想的范围。

【讨论】:

引用the docs 的话:BitSet 被设想为 _“根据需要增长的位向量。”这比您建议的典型用途要普遍得多。其他向量类(Vector、ArrayList 等)没有“移位”操作,但它们确实有“插入”和“删除”操作,可以有效地做同样的事情。 BitSet 具有类似的功能是有意义的,但事实并非如此。 (无序的)set 点很好,只是使用起来有些不合时宜。谢谢。 我质疑 BitSet 用于设置的观点。如果我在做设置,我要么在 int/long 中使用位,就像 20 年前的“真正的程序员”:-) 一样,或者更准确地说,我将使用 Enums 和 EnumSet。我更倾向于将 BitSets 用作稀疏/紧凑的 Set&lt;Integer&gt;

以上是关于为啥 Java `BitSet` 没有 `shiftLeft` 和 `shiftRight` 函数?的主要内容,如果未能解决你的问题,请参考以下文章

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

为啥 BitSet 不可迭代?

为啥 std::bitset 不带有迭代器?

一分钟轻松掌握 !Java 高级数据结构 -- 原生 BitSet 源码刨析

一分钟轻松掌握 !Java 高级数据结构 -- 原生 BitSet 源码刨析

一分钟轻松掌握 !Java 高级数据结构 -- 原生 BitSet 源码刨析