Array.Equals


“I wish .NET can compare contents of an Array.”  – Annoymous Array Comparer


Currently, when you compare two arrays with the = operator, we are really using the System.Object’s = operator, which only compares the instances. (i.e. this uses reference equality, so it will only be true if both arrays points to the exact same instance) 


Well until we add this feature request in the framework itself, I’ve provided a temporary solution for you:


    public static bool ArrayEquals<T>(T[] a, T[] b)


    {


        if (a.Length != b.Length)


            return false;


        for (int i = 0; i < a.Length; i++)


        {


            if (!a[i].Equals(b[i]))


                return false;


        }


        return true;


    }


Again, my solution is not perfect, because if you are comparing value types, I am boxing the value type before calling System.Object::Equal as you can see here:


IL_0026:  box        !!T
IL_002b:  constrained. !!T
IL_0031:  callvirt   instance bool [mscorlib]System.Object::Equals(object)


My office neighbour Brian Grunkemeyer have the following perf suggestions:


1) Add a generic constraint to this method


public static bool ArrayEquals<T>(T[] a, T[] b) where T: IEquatable<T>


This will help our performance, but limits the method to only take types that implement IEquatable<T>. This may be too restrictive for some though.


2) Use EqualityComparer<T>.Default’s Equal method instead of calling Equals on the types themselves.


    public static bool ArrayEquals<T>(T[] a, T[] b)


    {


        if (a.Length != b.Length)


            return false;


        EqualityComparer<T> comparer = EqualityComparer<T>.Default;


        for (int i = 0; i < a.Length; i++)


        {


            if (!comparer.Equals(a[i], b[i]))


                return false;


        }


        return true;


    }



Without measuring the perf of these on long arrays, my guess is that for value types #1 has the best perf, followed by #2 and then the original implementation.


What do you think?


Comments (10)

  1. sam says:

    the following method may get around the boxing issue for value types, its not how you would do this but it shows of anon. methods

               int[] a1 = new int[] { 2, 3, 4, 6, 7 };

               int[] a2 = new int[] { 2, 3, 4, 6, 7 };

               Predicate<int> d = delegate(int ii) {

                   foreach (int v in a2)

                   {

                       if (v==ii)

                       {

                           return true;

                       }

                   }

                   return false;

               };

               if (a1.Length==a2.Length)

               {

                   if (Array.TrueForAll<int>(a1, d))

                       MessageBox.Show("Arrays are equal");

                   else

                       MessageBox.Show("Arrays are not equal");

               }

  2. Chris Nahr says:

    Your first variant isn’t immediately usable because you have to check whether a[i] is a null reference before you can try invoking Equals on it. The additional checking code definitely makes the first variant longer than the second, although the speedup might be worth it.

  3. shrib says:

    You also have to think about the semantics of comparing an array of arrays. You may not care for the recursive comparison, or maybe you do. If you do want recursive comparison, you have to worry about loops in the object graph.

  4. Joshua says:

    If you’re looking for very generic code, without additional hastle or constraints you’re always going to end up checking null for reference types.

    I was wondering if this would work…  I havn’t actually tried it or debugged it or really thought about it much

    public bool IsEqual(T[] a, T[] b)

    {

       if ((a == null || b == null) &&

           (a != null || b != null))

           return false;

       if (a.Length != b.Length)

           return false;

       // HACK: And proud

       bool isRef = false;

       try

       {

           T moo = null;

           isRef = true;

       } catch { }

       if (isRef)

       {

           // Object comparrison with null check

           // a.Length == b.Length

           for (long l = 0; l < a.Length; l++)

           {

               if (a[l] != null && !a[l].Equals(b[l]))

               return false;

           }

       }

       else

       {

           // Value types

           // a.Length == b.Length

           for (long l = 0; l < a.Length; l++)

           {

               if (a[l] != b[l])

               return false;

           }

       }

       return true;

    }

  5. Tim says:

    This is a great example of something really simple that C# makes really hard. And it just happens that I’m feeling this exact pain today (array comparison), transitioning from C++.

  6. robert says:

    looks like the SequenceEqual extension in C#3.0 or 3.5 or whatever may be what we want!

  7. Peter says:

    SequenceEqual is not as fast as the method here…. not atleast as far as my tests show..

  8. //here is my solution tests and all…

    private static bool Validate<T>(T[] array1, T[] array2)

    {

    array1.ValidateIsNotNull("array1"); //Throw Exception if item is null…

    array2.ValidateIsNotNull("array2");

    if (array1.Length != array2.Length)

    {

    return false;

    }

    return true;

    }

    public static bool AreValuesEqual<T>(this T[] array1, T[] array2)

    where T : struct, IEquatable<T>

    {

    if (!Validate(array1, array2))

    {

    return false;

    }

    for (var i = 0; i < array1.Length; i++)

    {

    if(!array1[i].Equals(array2[i]))

    {

    return false;

    }

    }

    return true;

    }

    public static bool AreItemsEqual<T>(this T[] array1, T[] array2)

    where T : class, IEquatable<T>

    {

    if (!Validate(array1, array2))

    {

    return false;

    }

    for (var i = 0; i < array1.Length; i++)

    {

    var item1 = array1[i];

    var item2 = array2[i];

    if(item1 == null ^ item2 == null)

    {

    return false;

    }

    if (item1 != null && !item1.Equals(item2))

    {

    return false;

    }

    }

    return true;

    }

    public static bool AreItemsEqual(string[] array1, string[] array2, StringComparer comparer)

    {

    comparer.ValidateIsNotNull("comparer");

    if (!Validate(array1, array2))

    {

    return false;

    }

    for (var i = 0; i < array1.Length; i++)

    {

    if (!comparer.Equals(array1[i], array2[i]))

    {

    return false;

    }

    }

    return true;

    }

    [Test]

    public void TestMethod()

    {

    var bytes1 = new byte[] {1,2,3,4,79};

    var bytes2 = new byte[] {1,2,3,4,79};

    Assert.IsTrue(ArrayExtender.AreValuesEqual(bytes1, bytes2));

    var strs1 = new string[] {"123", "abc"};

    var strs2 = new string[] {"123", "aBc"};

    Assert.IsTrue(ArrayExtender.AreItemsEqual(strs1, strs2, StringComparer.OrdinalIgnoreCase));

    Assert.IsFalse(ArrayExtender.AreItemsEqual(strs1, strs2, StringComparer.Ordinal));

    }

    [Test]

    public void TestMethod2()

    {

    var bytes1 = new string[] {"123", "abc"};

    var bytes2 = new string[] {"123", "aBc"};

    Assert.IsTrue(ArrayExtender.AreItemsEqual(bytes1, bytes2, StringComparer.OrdinalIgnoreCase));

    }

    [Test]

    public void TestMethod3()

    {

    var strs1 = new string[] {"123", "abc"};

    var strs2 = new string[] {"123", null};

    Assert.IsFalse(ArrayExtender.AreItemsEqual(strs1, strs2, StringComparer.OrdinalIgnoreCase));

    strs1 = new string[] {"123", null};

    Assert.IsTrue(ArrayExtender.AreItemsEqual(strs1, strs2, StringComparer.OrdinalIgnoreCase));

    }

  9. Nick says:

    I know this is an old post and everyone is probably using Linq now, but should you call Sort on the two lists before you step through them?

Skip to main content