【问题标题】:C# byte array comparisonC#字节数组比较
【发布时间】:2010-11-26 06:00:15
【问题描述】:

我在使用 .NET 3.0 的 C# 中有两个字节数组。

比较两个字节数组是否包含每个元素的相同内容的“最有效”方法是什么?

例如,字节数组{0x1, 0x2}{0x1, 0x2} 相同。但是字节数组{0x1, 0x2}和字节数组{0x2, 0x1}是不一样的。

【问题讨论】:

标签: c# .net bytearray


【解决方案1】:

嗯,你可以使用:

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 位来使其在非托管代码中更快 - 但我不想即时编写代码。

【讨论】:

  • 对于大型数组,比如 10k,并行化是一种有效的优化,还是线程开销太大?
  • 您对 IStructuralEquatable 有什么看法?
  • @Lijo:我承认我从来没有完全理解过IStructuralEquatable 以及什么时候合适。
【解决方案2】:

您可以使用SequenceEqual 方法:

bool areEqual = firstArray.SequenceEqual(secondArray);

如 cmets 中所述,SequenceEqual 需要 .NET 3.5(或 LINQBridge,如果您使用的是 VS2008 并针对较早版本的框架)。

【讨论】:

  • 我认为最有效的方式。
  • 就执行时间而言不是。
  • @Veton:这肯定是打字最少的!有关其他一些优化,请参阅 Jon 的回答。
  • (尝试通过 SequenceEqual 将包含 1,000,000 个条目的字节数组与包含 1,000,001 个条目的字节数组进行比较。它会一直到最后才注意到它们的长度不同...)
  • 根据问题的要求,这在 .NET 3.0 中也不起作用(没有 LINQBridge 之类的东西)......尽管 OP 可能意味着 .NET 3.5。
【解决方案3】:

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;
}

安全代码得到了相当优化(例如,编译器知道它不必检查索引边界),所以我不认为不安全代码会更快。任何显着差异都来自一次比较多个字节的能力。

【讨论】:

  • 好概念,虽然代码无法编译:“不能分配给'p1',因为它是'固定变量'”
  • @Edward Brey:你是对的,那是行不通的。您需要在块内声明新指针以使其可更改。我已经更正了代码。
  • 在 comp 中使用 long(8 字节)会更快,即使在 32 位机器上也是如此。
【解决方案4】:

如果不是太在意性能,可以考虑IStructuralEquatable

.NET Framework 支持:4.5、4

结构相等意味着两个对象相等,因为它们具有相等的值。它不同于引用相等。

例子:

static bool ByteArrayCompare(byte[] a1, byte[] a2) 
{
  IStructuralEquatable eqa1 = a1;
  return eqa1.Equals(a2, StructuralComparisons.StructuralEqualityComparer);
}

参考

  1. What problem does IStructuralEquatable and IStructuralComparable solve?
  2. Why aren't IStructuralEquatable and IStructuralComparable generic?
  3. IStructuralEquatable Interface

【讨论】:

  • 给这篇文章一个要点:当我们使用最新版本的框架时,这可能是一种处理方式。
【解决方案5】:

如果你希望它真的很快,你可以使用不安全的代码(这并不总是可能的):

    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;
        }
    }

【讨论】:

    【解决方案6】:

    .NET 6 的更新。

    如今Enumerable.SequenceEqual 方法仍然是最佳选择

    var byteArray1 = new[]{0x01, 0x02};
    var byteArray2 = new[]{0x01, 0x02};
    
    bool isEqual = byteArray1.SequenceEqual(byteArray2);
    

    不过,另一个选项也可以开箱即用 - ReadOnlySpan<T>.SequenceEqual,例如:

    bool isEqual = new Span<byte>(byteArray1).SequenceEqual(new Span<byte>(byteArray2));
    

    Span 方法在 .NET 6 之前的版本中曾经更快,但 MS 人员大幅优化了 Enumerable.SequenceEqual 的实现,现在在后台使用 ReadOnlySpan&lt;T&gt;PR 1PR 2) .请参阅Performance improvements in .NET 6 了解更多信息。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-11-02
      • 1970-01-01
      • 2014-10-21
      • 2013-03-25
      • 1970-01-01
      • 2011-07-03
      • 1970-01-01
      相关资源
      最近更新 更多