George2
George2

Reputation: 45801

C# byte array comparison

I have two byte arrays in C# using .NET 3.0.

What is the "most efficient" way to compare whether the two byte arrays contains the same content for each element?

For example, byte array {0x1, 0x2} is the same as {0x1, 0x2}. But byte array {0x1, 0x2} and byte array {0x2, 0x1} are not the same.

Upvotes: 21

Views: 31099

Answers (6)

Alex Klaus
Alex Klaus

Reputation: 8954

Update for .NET 6.

These days Enumerable.SequenceEqual method is still the best option:

var byteArray1 = new[]{0x01, 0x02};
var byteArray2 = new[]{0x01, 0x02};

bool isEqual = byteArray1.SequenceEqual(byteArray2);

Though, another option is also available out-of-the-box – ReadOnlySpan<T>.SequenceEqual, e.g.:

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

The Span approach used to be faster in the pre-.NET 6 time, but the MS folks drastically optimised implementation of Enumerable.SequenceEqual that's now using ReadOnlySpan<T> under the hood (PR 1, PR 2). See Performance improvements in .NET 6 for more info.

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1503130

Well, you could use:

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

(I normally use braces for everything, but I thought I'd experiment with this layout style just for a change...)

This has a few optimisations which SequenceEqual can't (or doesn't) perform - such as the up-front length check. Direct array access will also be a bit more efficient than using the enumerator.

Admittedly it's unlikely to make a significant difference in most cases...

You could possibly make it faster in unmanaged code by making it compare 32 or 64 bits at a time instead of 8 - but I wouldn't like to code that on the fly.

Upvotes: 47

LCJ
LCJ

Reputation: 22661

If you are not too concerned about performance, you can consider IStructuralEquatable.

.NET Framework Supported in: 4.5, 4

Structural equality means that two objects are equal because they have equal values. It differs from reference equality.

Example:

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

REFERENCE

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

Upvotes: 4

Guffa
Guffa

Reputation: 700720

Jon mentioned comparing multiple bytes at once using unsafe code, so I had to give it a go:

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

The safe code gets pretty optimised (the compiler knows that it doesn't have to check index boundaries for example), so I wouldn't expect the unsafe code to be very much faster. Any significant difference would come from the ability to compare several bytes at once.

Upvotes: 5

LukeH
LukeH

Reputation: 269588

You can use the SequenceEqual method:

bool areEqual = firstArray.SequenceEqual(secondArray);

As mentioned in the comments, SequenceEqual requires .NET 3.5 (or LINQBridge if you're using VS2008 and targeting an earlier version of the framework).

Upvotes: 29

Philippe Leybaert
Philippe Leybaert

Reputation: 171864

If you want it to be really fast, you can use unsafe code (which isn't always possible):

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

Upvotes: 2

Related Questions