简体   繁体   中英

Easiest way to compare arrays in C#

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#?

You could use Enumerable.SequenceEqual . This works for any IEnumerable<T> , not just arrays.

Use Enumerable.SequenceEqual in LINQ .

int[] arr1 = new int[] { 1,2,3};
int[] arr2 = new int[] { 3,2,1 };

Console.WriteLine(arr1.SequenceEqual(arr2)); // false
Console.WriteLine(arr1.Reverse().SequenceEqual(arr2)); // true

Also for arrays (and tuples) you can use new interfaces from .NET 4.0: IStructuralComparable and IStructuralEquatable . Using them you can not only check equality of arrays but also compare them.

static class StructuralExtensions
{
    public static bool StructuralEquals<T>(this T a, T b)
        where T : IStructuralEquatable
    {
        return a.Equals(b, StructuralComparisons.StructuralEqualityComparer);
    }

    public static int StructuralCompare<T>(this T a, T b)
        where T : IStructuralComparable
    {
        return a.CompareTo(b, StructuralComparisons.StructuralComparer);
    }
}

{
    var a = new[] { 1, 2, 3 };
    var b = new[] { 1, 2, 3 };
    Console.WriteLine(a.Equals(b)); // False
    Console.WriteLine(a.StructuralEquals(b)); // True
}
{
    var a = new[] { 1, 3, 3 };
    var b = new[] { 1, 2, 3 };
    Console.WriteLine(a.StructuralCompare(b)); // 1
}

For .NET 4.0 and higher you can compare elements in array or tuples via using StructuralComparisons type:

object[] a1 = { "string", 123, true };
object[] a2 = { "string", 123, true };

Console.WriteLine (a1 == a2);        // False (because arrays is reference types)
Console.WriteLine (a1.Equals (a2));  // False (because arrays is reference types)

IStructuralEquatable se1 = a1;
//Next returns True
Console.WriteLine (se1.Equals (a2, StructuralComparisons.StructuralEqualityComparer)); 

SequenceEqual will only return true if two conditions or met.

  1. They contain the same elements.
  2. The elements are in the same order.

If you only want to check if they contain the same elements regardless of their order and your problem is of the type

Does values2 contain all the values contained in values1?

you can use LINQ extension method Enumerable.Except and then check if the result has any value. Here's an example

int[] values1 = { 1, 2, 3, 4 };
int[] values2 = { 1, 2, 5 };
var result = values1.Except(values2);
if(result.Count()==0)
{
   //They are the same
}
else
{
    //They are different
}

And also by using this you get the different items as well automatically. Two birds with one stone.

Keep in mind, if you execute your code like this

var result = values2.Except(values1);

you will get different results.

In my case I have a local copy of an array and want to check if anything has been removed from the original array so I use this method.

If you would like to handle null inputs gracefully, and ignore the order of items, try the following solution:

static class Extensions
{
    public static bool ItemsEqual<TSource>(this TSource[] array1, TSource[] array2)
    {
        if (array1 == null && array2 == null)
            return true;
        if (array1 == null || array2 == null)
            return false;
        if (array1.Count() != array2.Count())
            return false;
        return !array1.Except(array2).Any() && !array2.Except(array1).Any();
    }
}

The test code looks like:

public static void Main()
{
    int[] a1 = new int[] { 1, 2, 3 };
    int[] a2 = new int[] { 3, 2, 1 };
    int[] a3 = new int[] { 1, 3 };
    Console.WriteLine(a1.ItemsEqual(a2)); // Output: True.
    Console.WriteLine(a2.ItemsEqual(a3)); // Output: False.
    Console.WriteLine(a3.ItemsEqual(a2)); // Output: False.
   
    int[] a4 = new int[] { 1, 1 };
    int[] a5 = new int[] { 1, 2 };
    Console.WriteLine(a4.ItemsEqual(a5)); // Output: False 
    Console.WriteLine(a5.ItemsEqual(a4)); // Output: False 
    
    int[] a6 = null;
    int[] a7 = null;
    int[] a8 = new int[0];

    Console.WriteLine(a6.ItemsEqual(a7)); // Output: True. No Exception.
    Console.WriteLine(a8.ItemsEqual(a6)); // Output: False. No Exception.
    Console.WriteLine(a7.ItemsEqual(a8)); // Output: False. No Exception.
}

For unit tests, you can use CollectionAssert.AreEqual instead of Assert.AreEqual .

It is probably the easiest way.

对于某些应用程序可能会更好:

string.Join(",", arr1) == string.Join(",", arr2)

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

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

elementwise compare ? what about

public void Linq78a()
{
 int[] numbers1 = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
 int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
 bool bb = numbers.Zip(numbers1, (a, b) => (a == b)).Any(p => !p);
 if (!bb) Console.WriteLine("Lists are equal (bb)");
   else Console.WriteLine("Lists are not equal (bb)");
}

Replace the (a==b) condition by anything you'd like to compare in a and b.

(this combines two examples from MSDN developer Linq samples )

I did this in visual studios and it worked perfectly; comparing arrays index by index with short this code.

private void compareButton_Click(object sender, EventArgs e)
        {
            int[] answer = { 1, 3, 4, 6, 8, 9, 5, 4, 0, 6 };
            int[] exam = { 1, 2, 3, 6, 8, 9, 5, 4, 0, 7 };

            int correctAnswers = 0;
            int wrongAnswers = 0;

            for (int index = 0; index < answer.Length; index++)
            {
                if (answer[index] == exam[index])
                {
                    correctAnswers += 1;
                }
                else
                {
                    wrongAnswers += 1;
                }
            }

            outputLabel.Text = ("The matching numbers are " + correctAnswers +
                "\n" + "The non matching numbers are " + wrongAnswers);
        }

the output will be; The matching numbers are 7 The non matching numbers are 3

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");

检查此线程的答案, 该线程将数组之一转换为HashSet并使用SetEquals与另一个数组进行比较。

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.

如果需要比较顺序不同的数组,也可以使用array1.ToList().All(x => array2.Contains(x))

        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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM