25

I am trying to compare two .NET arrays. Here is an obvious implementation for comparing arrays of bytes:

bool AreEqual(byte[] a, byte[] b){
    if(a.Length != b.Length)
        return false;
    for(int i = 0; i < a.Length; i++)
        if(a[i] != b[i])
            return false;

    return true;
}

A more refined approach can be seen here (via Google).

  1. What is the simplest way (using less code but readable) to compare two .NET arrays?
  2. What is the most efficient way compare two .NET arrays?
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Agnel Kurian
  • 57,975
  • 43
  • 146
  • 217

4 Answers4

49

You could use SequenceEqual:

string[] a = { "1", "2", "3" };
string[] b = { "1", "2", "3" };

bool areEqual = a.SequenceEqual(b); // true


string[] c = { "1", "2", "5" };
areEqual = a.SequenceEqual(c);      // false
Christian C. Salvadó
  • 807,428
  • 183
  • 922
  • 838
  • 3
    This [page](http://www.dotnetperls.com/sequenceequal) has some performance information and claims that `SequenceEqual` is about 10 times slower than custom implementation using a loop. I wonder why. – Roland Pihlakas Jun 12 '12 at 15:42
  • 2
    The `SequenceEqual` method is not available by default, you have to use `System.Linq`. Nice to know in case you get confused (as I did). – Arne Dec 07 '12 at 20:45
18

Kathy's approach seems a good one to me. I'd personally allow the comparer to be specified explicitly:

bool AreEqual<T>(T[] a, T[] b)
{
    return AreEqual(a, b, EqualityComparer<T>.Default);
}

bool AreEqual<T>(T[] a, T[] b, IEqualityComparer<T> comparer)
{
    // Handle identity comparison, including comparing nulls
    if (a == b)
    {
        return true;
    }

    if (a == null || b == null)
    {
        return false;
    }

    if(a.Length != b.Length)
    {
        return false;
    }

    for(int i = 0; i < a.Length; i++)
    {
        if(!comparer.Equals(a[i], b[i]))
        {
            return false;
        }
    }
    return true;
}

SequenceEqual as mentioned by CMS is good, but due to its generality over IEnumerable<T> I don't think it can do the "early out" if the length aren't equal. (It's possible that it checks for both sequences implementing IList though, to check Count directly.) You could generalise a little more, to use IList<T>

bool AreEqual<T>(IList<T> a, IList<T> b, IEqualityComparer<T> comparer)
{
    if(a.Count != b.Count)
    {
        return false;
    }
    for(int i = 0; i < a.Count; i++)
    {
        if(!comparer.Equals(a[i], b[i]))
        {
            return false;
        }
    }
    return true;
}

The straight array version will probably be the most efficient - adding generality and abstraction usually hits performance, although whether it's significant will depend on your app.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 4
    Shouldn't the above code snippets have `if (!comparer.Equals(a[i], b[i]) return false` ? Did I just detect an anomaly in my universe :) – Gishu Oct 05 '10 at 12:38
  • also requires a closing parentheses after the second 'if' statement. – Agnel Kurian Jun 21 '11 at 12:32
  • @Jon Could you please explain what is the advantage of explicitly specifying the comparer? – Sandeep Jun 26 '11 at 20:00
  • @Sandeep: Suppose you want to be able to compare two string arrays for equality, but using a case-insensitive comparison, so `{"FOO", "BAR"}` is equal to `{"foo", "bar"}`. – Jon Skeet Jun 26 '11 at 20:30
  • Shouldn't the second code snippet also use `!comparer.Equals(...)`? – jv42 Sep 20 '12 at 14:19
  • Also, these methods will throw an exception if at least one of the arrays is null, which might be wanted or not, especially: `AreEquals(null, null)` could be a desirable supported case (returning `true`). – jv42 Sep 20 '12 at 15:50
  • @jv42: Yes, you could do that - or throw ArgumentNullException. – Jon Skeet Sep 20 '12 at 15:58
  • You should use ICollection instead of IList if you want to check the Count as it defines what all .NET collections should implement (arrays, lists, sets, queues etc.). I disagree with the null == null comparison though. +1 for the EqualityComparer – Shelakel Dec 17 '13 at 09:38
  • @Shelakel: I'm using the indexer of `IList` as well. Why would you disagree with `null == null`? That's what most equality operators do in C#... making null not equal to itself would be odd in the context of .NET, IMO. – Jon Skeet Dec 17 '13 at 09:49
  • @JonSkeet: Using the indexer should be faster, unfortunately there's no shared ICollection like interface supporting indexers - guess the best approach would then to implement this for arrays and lists and ICollection and IEnumerable as fallbacks. Before the length check it's probably a good idea to do a ReferenceEquals. The primary reason why I'm saying null != null is due to nothing is actually being compared between a and b (no references, hash code, members, behaviour); if equality requires measurable value, then null != null. – Shelakel Dec 17 '13 at 11:24
  • @Shelakel: Yes, if you need to implement this for `ICollection` there are other alternatives, but they're not required for this question. As for nullity, if `a` and `b` are both null, then those *are* references being compared... and that's all the information that's available. `null == null` pretty much everywhere in .NET. If equality "requires measurable value" then you probably shouldn't be using it with `null` in the first place. – Jon Skeet Dec 17 '13 at 11:33
  • I think it would better to change if(a == null && b == null) to if(a==b) so it checks both null and same reference. Two collection with same reference are always same. – Mohammad Nikravan Aug 31 '16 at 11:23
9

With the advent of .NET 4 you can use the method Equals() being provided by .NET arrays' explicitly implemented interface IStructuralEquatable. Then the code might look like this (I rewrote CMS' example):

string[] a = { "1", "2", "3" };
string[] b = { "1", "2", "3" };
bool result = ((IStructuralEquatable)a).Equals(b, StructuralComparisons.StructuralEqualityComparer);
// result evaluates to true.

(IStructuralEquatable is also implemented in Tuples (also new in .NET 4).)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nico
  • 1,554
  • 1
  • 23
  • 35
0

Maybe something like this?

static bool AreEqual<T>(T[] a, T[] b) 
{
    bool areEqual = false ;
    T[] result = a.Intersect(b.AsEnumerable()).ToArray();
    areEqual = (result.Length == a.Length) && (result.Length == b.Length);
    return areEqual;
}

I am not sure of the performance hit on this one though.

EDIT

revised version taking into account Jon suggestions:

    static bool AreEqual<T>(T[] a, T[] b) 
    {
        return a.SequenceEqual(b);
    }
Igor Zelaya
  • 4,167
  • 4
  • 35
  • 52
  • If you can use LINQ and don't mind the performance hit over using arrays directly (and early out for differing lengths), SequenceEqual is the way forward. – Jon Skeet Jan 28 '09 at 07:43
  • 1
    This solution fails when there are duplicates: { 1, 1, 1} = { 1, 1, 1} returns false. It also doesn't take ordering into account: { 1, 2} != {2, 1} returns true. – Jon Skeet Jan 28 '09 at 07:49
  • "{ 1, 2} != {2, 1} returns true" should mean that ordering *is* taken into account, right? – Agnel Kurian Jan 28 '09 at 08:14
  • I'm saying that ordering *should* be taking into account, but isn't. The two arrays should be seen to be different, but the original version of AreEqual in this answer returns true. Sorry for the confusion. – Jon Skeet Jan 28 '09 at 09:31
  • If you want to check if 2 sets have the same unique values then maybe use something like: `return (new HashSet(a)).SetEquals(b);` (this ignores order and don't care if the same value exists more then once. – NiKiZe Jul 28 '20 at 08:25