在C#中比较数组的最简单方法

249
在Java中,Arrays.equals()函数允许轻松比较两个基本数组的内容(为所有基本类型提供了重载版本)。
在C#中是否有类似的方法?是否有任何“神奇”的方法可以比较两个数组的内容?

5
阅读本文的每个人,请记住被接受的答案使用SequenceEqual。SequenceEqual不仅会检查它们是否包含相同的数据,还会检查它们是否按照相同的顺序包含相同的数据。 - John Demetriou
19个回答

359

4
只有在它们顺序相同时才能起作用。 - John Demetriou
3
从性能的角度考虑,SequenceEqual可能不是一个好的选择,因为它的当前实现方式会完全枚举其中一个源,如果它们仅在长度上有所不同。对于数组,我们可以先检查它们的长度是否相等,以避免枚举不同长度的数组最终只得到 false - Frédéric
5
@Frédéric,实际上SequenceEqual会首先比较长度,如果两个可枚举项都实现了ICollection,请参见https://github.com/dotnet/runtime/blob/main/src/libraries/System.Linq/src/System/Linq/SequenceEqual.cs - quinmars
4
@quinmars,这个答案是关于 .Net Framework 的,.Net Core 在 2010 年还不存在。相关代码在 这里。虽然现在 .Net Core 已经改变了这一点,但对于 .Net Framework 仍然是正确的。 - Frédéric
为什么这不是唯一推荐的答案,只是好奇。 - Shahid Roofi Khan

112

LINQ 中使用 Enumerable.SequenceEqual

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

3
请记住,这会针对空参数抛出异常,因此请确保不要假设 new int[] {1}.SequenceEquals(null) == false - sara

32

此外,针对数组(和元组),您可以使用.NET 4.0中的新接口:IStructuralComparableIStructuralEquatable。使用它们,您不仅可以检查数组的相等性,还可以比较它们。

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
}

2
在大型值类型数组上使用它们会导致性能下降,因为它们的当前实现将装箱每个值以进行比较。 - Frédéric

24

SequenceEqual 仅在满足以下两个条件时返回 true:

  1. 它们包含相同的元素。
  2. 这些元素的顺序相同。

如果您只想检查它们是否包含相同的元素而不考虑其顺序,且您的问题是以下类型:

值 2 是否包含值 1 中包含的所有值?

您可以使用 LINQ 扩展方法 Enumerable.Except,然后检查结果是否有任何值。以下是一个示例:

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
}

同时使用它,您还可以自动获得不同的物品。一箭双雕。

请记住,如果像这样执行您的代码

var result = values2.Except(values1);

你会得到不同的结果。

在我的情况下,我拥有一个数组的本地副本,并且想要检查原始数组是否已经删除了任何内容,因此我使用这种方法。


21

对于.NET 4.0及更高版本,您可以使用StructuralComparisons类型比较数组或元组中的元素:

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

20

如果你想要优雅地处理null输入,并且忽略项目的顺序,请尝试以下解决方案:

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

测试代码如下:

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

很棒的答案。不知道如何将其扩展到二维数组。 - Robinson

12

对于单元测试,你可以使用CollectionAssert.AreEqual而不是Assert.AreEqual

这可能是最简单的方法。


7

对于一些应用程序来说,以下方案可能更加优秀:

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

低估的解决方案!这对许多用例非常完美。 - Thomas
@Thomas 实际上他们投了几次反对票 ;) - alexkovelsky

7
假设数组相等意味着两个数组在相同的索引处具有相等的元素,那么这里有SequenceEqual答案IStructuralEquatable答案。

但是从性能角度来看,两者都有缺点。

.Net Framework中SequenceEqual实现在数组长度不同时不会进行快捷方式,因此它可能完全枚举其中一个数组,比较其每个元素。
这就是说,根据.Net版本(例如.Net5),它可能会进行快捷方式,请参见
此评论。因此,对于最新的.Net项目,SequenceEqual应该是一个不错的选择。

IStructuralEquatable不是泛型的,并且可能导致每个比较值的装箱。此外,它不是非常直观易用的,已经要求编写一些帮助方法将其隐藏起来。

从性能角度来看,使用以下内容可能更好:

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

当然,这并不是检查数组相等的某种“魔法方式”。

目前,在 .Net 中没有真正等同于 Java Arrays.equals() 的方法。


2
这个LINQ解决方案可行,但不确定其在性能上与SequenceEquals相比如何。但它处理不同的数组长度,并且.All会在第一个不相等的项处退出而不必迭代整个数组。"最初的回答"
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)
            );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接