位arrays相等

我需要比我的应用程序中的System.Collections.BitArray类更多的东西。 具体来说,我需要位数组:

  • 是不可改变的
  • 使用值语义实现相等性

我创建了自己的struct ,主要是复制BitArray实现的内部。 (谢谢, .Net Reflector !)

我不会每天处理按位操作,所以我对我的平等实现没有最高的信心。 (它正在通过我投掷的unit testing,但我可能会遗漏边缘情况。)我将我提出的解决方案作为下面的答案。 我会感谢别人的反馈和答案,可能更正确或更有效。

就像CLR BitArraylength字段指的是struct中的位数,而array字段(或Array属性)指的是表示位的32位整数数组。

[澄清]我选择在构造函数和其他方法中采用简单的路径,这样我就不能依赖于不必要的位为零。 例如,

  • Not()通过整数数组元素上的按位求反( ~ )来实现。
  • 可以使用构造函数,它使用length和boolean来初始化所有位。 如果初始化值为true,我将int数组的所有元素设置为-1(以二进制补码表示,由全1表示)
  • 等等。

因此,我需要在比较中处理(或者更确切地说,忽略)它们。 一个很好的解决方案也是始终将这些位置零,但在我的情况下会导致更多的工作(对于计算机和我来说都是如此!)

更新 :我原来的分析不正确……

不幸的是,我对<< 32 - C#的行为不正确,强制左移运算符将限制向右操作数的低5位的移位数(对于涉及64位左操作数的移位,为6位) 。 因此,您的原始代码在C#中定义良好且正确(在C / C ++中是未定义的行为)。 基本上,这个转变表达式:

 (this.Array[i] << shift) 

相当于:

 (this.Array[i] << (shift & 0x1f)) 

我可能仍然会改变这种转变以明确这一点(如果没有其他原因,当我在6个月之后查看该代码时,我不会偶然发现同样的错误分析)使用上面而不是if (shift == 32)检查。

原始分析:


好的,所以这是第二个答案。 最重要的是,我认为你的原始解决方案有一个错误,在你的ImmutableBitArray的位长是32位的倍数的情况下,你将为最后一个Int32[]数组元素不同的2个数组返回true

例如,考虑具有32位不同的ImmutableBitArray 。 原始的Equals()方法将对数组中唯一的Int32执行移位操作 - 但它会将值移位32位,因为

 int shift = 0x20 - (this.length % 0x20); 

将评估为32。

这意味着下一个测试:

 if (this.Array[i] << shift != other.Array[i] << shift) 

将测试(0 != 0)因此将不执行return false

我将你的Equals()方法更改为以下内容,这不是一个重大改变 - 我认为它会处理上面提到的错误并更改其他一些严格与样式相关的内容,因此可能没有对你感兴趣。 另请注意,我实际上没有编译和测试我的Equals()方法,因此有几乎100%的可能存在错误(或至少有语法错误):

 public bool Equals(ImmutableBitArray other) { if (this.length != other.length) { return false; } int finalIndex = this.Array.Length - 1; for (int i = 0; i < finalIndex; i++) { if (this.Array[i] != other.Array[i]) { return false; } } // check the last array element, making sure to ignore padding bits int shift = 32 - (this.length % 32); if (shift == 32) { // the last array element has no padding bits - don't shift shift = 0; } if (this.Array[finalIndex] << shift != other.Array[finalIndex] << shift) { return false; } return true; } 

请注意,严格来说,即使原始的GetHashCode()方法具有相同的缺陷,也不会出现错误,因为即使您在位长为32的倍数时没有正确混合最后一个元素,相同的对象也会仍然返回相同的哈希码。 但我仍然可能决定在GetHashCode()中以相同的方式解决这个缺陷。

如果在ImmutableBitArray的构造函数中,最后一个元素上未使用的“填充位”被强制为零,则不需要跳过箍只检查最后一个元素中的有效位,因为填充将是相同的实例。

这将很好地简化Equals()GetHashCode()方法:

 public bool Equals(ImmutableBitArray other) { if (this.length != other.length) { return false; } for (int i = 0; i < this.Array.Length; i++) { if (this.Array[i] != other.Array[i]) { // since padding bits are forced to zero in the constructor, // we can test those for equality just as well and the valid // bits return false; } } return true; } public override int GetHashCode() { int hc = this.length; for (int i = 0; i < this.Array.Length; i++) { // since padding bits are forced to zero in the constructor, // we can mix those into the hashcode no problem hc ^= this.Array[i]; } return hc; } 

平等方法:

 public bool Equals(ImmutableBitArray other) { if (this.length != other.length) { return false; } for (int i = 0; i < this.Array.Length; i++) { if (this.Array[i] != other.Array[i]) { // This does not necessarily mean that the relevant bits of the integer arrays are different. // Is this before the last element in the integer arrays? if (i < this.Array.Length - 1) { // If so, then the objects are not equal. return false; } // If this is the last element in the array we only need to be concerned about the bits // up to the length of the bit array. int shift = 0x20 - (this.length % 0x20); if (this.Array[i] << shift != other.Array[i] << shift) { return false; } } } return true; } 

并且必要的GetHashCode覆盖:

 public override int GetHashCode() { int hc = this.length; for (int i = 0; i < this.Array.Length; i++) { if (i < this.Array.Length - 1) { hc ^= this.Array[i]; } else { int shift = 0x20 - (this.length % 0x20); hc ^= this.Array[this.Array.Length - 1] << shift; } } return hc; } 

经过几个小时的搜索和学习,我终于得到了答案,并想分享。 我没有审查性能,因为我只关心可读性。

 if (input1.length != input2.length) { return false; } var result = new BitArray(input1); result = result.Xor(input2); if (result.Cast().Contains(true)) return false; return true;