Easiest way to compare arrays in C#

Viewed 271966

In Java, Arrays.equals() allows to easily compare the content of two basic arrays (overloads are available for all the basic types).

Is there such a thing in C#? Is there any "magic" way of comparing the content of two arrays in C#?

18 Answers

Assuming array equality means both arrays have equal elements at equal indexes, there is the SequenceEqual answer and the IStructuralEquatable answer.

But both have drawbacks, performance wise.

SequenceEqual implementation in .Net Framework will not shortcut when the arrays have different lengths, and so it may enumerate one of them entirely, comparing each of its elements.
This said, depending on the .Net flavor (like .Net5), it may shortcut, see this comment. So for an up-to-date .Net project, SequenceEqual should be a good choice.

IStructuralEquatable is not generic and may cause boxing of each compared value. Moreover it is not very straightforward to use and already calls for coding some helper methods hiding it away.

It may be better, performance wise, to use something like:

bool ArrayEquals<T>(T[] first, T[] second)
{
    if (first == second)
        return true;
    if (first == null || second == null)
        return false;
    if (first.Length != second.Length)
        return false;
    for (var i = 0; i < first.Length; i++)
    {
        if (!first[i].Equals(second[i]))
            return false;
    }
    return true;
}

But of course, that is not either some "magic way" of checking array equality.

So currently, no, there is not really an equivalent to Java Arrays.equals() in .Net.

This LINQ solution works, not sure how it compares in performance to SequenceEquals. But it handles different array lengths and the .All will exit on the first item that is not equal without iterating through the whole array.

private static bool arraysEqual<T>(IList<T> arr1, IList<T> arr2)
        =>
            ReferenceEquals(arr1, arr2) || (
                arr1 != null && arr2 != null &&
                arr1.Count == arr2.Count &&
                arr1.Select((a, i) => arr2[i].Equals(a)).All(i => i)
            );

You can use Enumerable.Intersect:

int[] array1 = new int[] { 1, 2, 3, 4,5 },
      array2 = new int[] {7,8};

if (array1.Intersect(array2).Any())
    Console.WriteLine("matched");
else
    Console.WriteLine("not matched");

I was looking to determine if two sets had equivalent contents, in any order. That meant that, for each element in set A there were equal numbers of elements with that value in both sets. I wanted to account for duplicates (so {1,2,2,3} and {1,2,3,3} should not be considered "the same").

This is what I came up with (note that IsNullOrEmpty is another static extension method that returns true if the enumerable is null or has 0 elements):

    public static bool HasSameContentsAs<T>(this IEnumerable<T> source, IEnumerable<T> target) 
        where T : IComparable
    {
        //If our source is null or empty, then it's just a matter of whether or not the target is too
        if (source.IsNullOrEmpty())
            return target.IsNullOrEmpty();

        //Otherwise, if the target is null/emtpy, they can't be equal
        if (target.IsNullOrEmpty())
            return false;

        //Neither is null or empty, so we'll compare contents.  To account for multiples of 
        //a given value (ex. 1,2,2,3 and 1,1,2,3 are not equal) we'll group the first set
        foreach (var group in source.GroupBy(s => s))
        {
            //If there are a different number of elements in the target set, they don't match
            if (target.Count(t => t.Equals(group.Key)) != group.Count())
                return false;
        }

        //If we got this far, they have the same contents
        return true;
    }

If you don't want to compare the order but you do want to compare the count of each item, including handling null values, then I've written an extension method for this.

It gives for example the following results:

new int?[]{  }.IgnoreOrderComparison(new int?{ });                            // true
new int?[]{ 1 }.IgnoreOrderComparison(new int?{ });                           // false
new int?[]{ }.IgnoreOrderComparison(new int?{ 1 });                           // false
new int?[]{ 1 }.IgnoreOrderComparison(new int?{ 1 });                         // true
new int?[]{ 1, 2 }.IgnoreOrderComparison(new int?{ 2, 1 });                   // true
new int?[]{ 1, 2, null }.IgnoreOrderComparison(new int?{ 2, 1 });             // false
new int?[]{ 1, 2, null }.IgnoreOrderComparison(new int?{ null, 2, 1 });       // true
new int?[]{ 1, 2, null, null }.IgnoreOrderComparison(new int?{ null, 2, 1 }); // false
new int?[]{ 2 }.IgnoreOrderComparison(new int?{ 2, 2 });                      // false
new int?[]{ 2, 2 }.IgnoreOrderComparison(new int?{ 2, 2 });                   // true

Here is the code:

public static class ArrayComparisonExtensions
{
    public static bool IgnoreOrderComparison<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second) =>
        IgnoreOrderComparison(first, second, EqualityComparer<TSource>.Default);

    public static bool IgnoreOrderComparison<TSource>(this IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
    {
        var a = ToDictionary(first, out var firstNullCount);
        var b = ToDictionary(second, out var secondNullCount);

        if (a.Count != b.Count)
            return false;

        if (firstNullCount != secondNullCount)
            return false;

        foreach (var item in a)
        {
            if (b.TryGetValue(item.Key, out var count) && item.Value == count)
                continue;
            return false;
        }


        return true;

        Dictionary<TSource, int> ToDictionary(IEnumerable<TSource> items, out int nullCount)
        {
            nullCount = 0;
            var result = new Dictionary<TSource, int>(comparer);
            foreach (var item in items)
            {
                if (item is null)
                    nullCount++;
                else if (result.TryGetValue(item, out var count))
                    result[item] = count + 1;
                else
                    result[item] = 1;
            }

            return result;
        }
    }
}

It only enumerates each enumerable once, but it does create a dictionary for each enumerable and iterates those once too. I'd be interested in ways to improve this.

Check the answer to this thread which converts one of the array to a HashSet and uses SetEquals for comparison with the other array. Note however that this does not check for order or duplicates.

You can also use array1.ToList().All(x => array2.Contains(x)) if you need to compare arrays which have not the same order

        int[] a = { 2, 1, 3, 4, 5, 2 };

        int[] b = { 2, 1, 3, 4, 5, 2 };

        bool ans = true;

        if(a.Length != b.Length)
        {
            ans = false;
        }
        else
        {
            for (int i = 0; i < a.Length; i++)
            {
                if( a[i] != b[i])
                {
                    ans = false;
                }
            }
        }

        string str = "";

        if(ans == true)
        {
            str = "Two Arrays are Equal";
        }

        if (ans == false)
        {
            str = "Two Arrays are not Equal";
        }

       //--------------Or You can write One line of Code-------------

        var ArrayEquals = a.SequenceEqual(b);   // returns true
Related