我在使用 .NET 3.0 的 C# 中有两个字节数组。
如何最有效地比较这两个字节数组的每个元素是否包含相同的内容?
例如,字节数组 {0x1, 0x2}
和 {0x1, 0x2}
是相同的。但是字节数组 {0x1, 0x2}
和字节数组 {0x2, 0x1}
不相同。
好的,您可以使用:
public static bool ByteArraysEqual(byte[] b1, byte[] b2)
{
if (b1 == b2) return true;
if (b1 == null || b2 == null) return false;
if (b1.Length != b2.Length) return false;
for (int i=0; i < b1.Length; i++)
{
if (b1[i] != b2[i]) return false;
}
return true;
}
(我通常对所有内容都使用大括号,但是我想尝试这种布局风格,只是为了改变一下...)
这种方法有一些优化,SequenceEqual
不能(或者不会)执行 - 比如提前进行的长度检查。直接访问数组也比使用枚举器更加高效。
诚然,在大多数情况下,这可能不会产生显着差异...
你可以通过在非托管代码中使其每次比较32或64位而不是8位来可能使其更快 - 但我不想即兴编写该代码。
IStructuralEquatable
是否合适? - LCJIStructuralEquatable
以及何时使用它是合适的。 - Jon SkeetSequenceEqual
方法:bool areEqual = firstArray.SequenceEqual(secondArray);
如评论中所提到的,SequenceEqual
需要 .NET 3.5(或LINQBridge如果您正在使用VS2008并针对较早版本的框架进行目标设置)。
Jon提到使用不安全的代码一次比较多个字节,所以我不得不试一试:
public unsafe bool ByteArraysEqual(byte[] b1, byte[] b2) {
if (b1 == b2) return true;
if (b1 == null || b2 == null) return false;
if (b1.Length != b2.Length) return false;
int len = b1.Length;
fixed (byte* p1 = b1, p2 = b2) {
int* i1 = (int*)p1;
int* i2 = (int*)p2;
while (len >= 4) {
if (*i1 != *i2) return false;
i1++;
i2++;
len -= 4;
}
byte* c1 = (byte*)i1;
byte* c2 = (byte*)i2;
while (len > 0) {
if (*c1 != *c2) return false;
c1++;
c2++;
len--;
}
}
return true;
}
安全代码得到了很好的优化(编译器知道不必检查索引边界),因此我不会指望不安全的代码更快。任何显著的差异都来自于能够一次比较多个字节。
如果您不太关心性能,可以考虑使用IStructuralEquatable
。
.NET Framework支持版本:4.5、4
结构相等意味着两个对象相等,因为它们具有相等的值。这与引用相等不同。
示例:
static bool ByteArrayCompare(byte[] a1, byte[] a2)
{
IStructuralEquatable eqa1 = a1;
return eqa1.Equals(a2, StructuralComparisons.StructuralEqualityComparer);
}
参考资料
.NET 6
更新。
目前来看,Enumerable.SequenceEqual 方法仍然是最佳选择:
var byteArray1 = new[]{0x01, 0x02};
var byteArray2 = new[]{0x01, 0x02};
bool isEqual = byteArray1.SequenceEqual(byteArray2);
bool isEqual = new Span<byte>(byteArray1).SequenceEqual(new Span<byte>(byteArray2));
Span
方法曾经更快,但是微软的开发团队大幅度优化了Enumerable.SequenceEqual
的实现,在底层使用 ReadOnlySpan<T>
(PR 1,PR 2)。有关更多信息,请参见 .NET 6 中的性能改进。如果你想让它运行得更快,你可以使用不安全的代码(这并不总是可行):
public static bool ArraysEqual(byte[] b1, byte[] b2)
{
unsafe
{
if (b1.Length != b2.Length)
return false;
int n = b1.Length;
fixed (byte *p1 = b1, p2 = b2)
{
byte *ptr1 = p1;
byte *ptr2 = p2;
while (n-- > 0)
{
if (*ptr1++ != *ptr2++)
return false;
}
}
return true;
}
}