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

在Java中,Arrays.equals()允许轻松地比较两个基本数组的内容(重载可用于所有基本类型)。

c#中有这样的东西吗?在c#中是否存在比较两个数组内容的“神奇”方法?

277596 次浏览

你可以使用Enumerable.SequenceEqual。这适用于任何IEnumerable<T>,而不仅仅是数组。

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

对于数组(和元组),你也可以使用。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
}

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

SequenceEqual只有在满足两个条件时才返回true。

  1. 它们包含相同的元素。
  2. 元素的顺序是一样的。

如果您只想检查它们是否包含相同的元素,而不管它们的顺序如何,那么您的问题属于

values2是否包含values1中包含的所有值?

你可以使用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);

你会得到不同的结果。

在我的例子中,我有一个数组的本地副本,想要检查是否有任何东西已经从原始数组中删除,所以我使用这个方法。

Elementwise比较?是什么

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

将(a==b)条件替换为您想在a和b中进行比较的任何内容。

(这结合了MSDN开发人员Linq示例中的两个例子)

我在视觉工作室做了这个,效果非常好;用简短的此代码逐个索引地比较数组。

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

输出将是;匹配的数字是7 不匹配的数字是3

对于单元测试,你可以使用CollectionAssert.AreEqual代替Assert.AreEqual

这可能是最简单的方法。

如果你想优雅地处理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.
}

对于某些应用可能更好:

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

假设数组相等意味着两个数组在相等的下标处有相等的元素,则存在SequenceEqual回答IStructuralEquatable回答

但两者在性能方面都有缺点。

SequenceEqual在. net Framework中的实现将不会在数组具有不同长度时执行快捷键,因此它可能会完全枚举其中一个数组,比较其中的每个元素。
也就是说,取决于。net的风格(比如。net 5),它可能是快捷方式,参见这样的评论。因此,对于一个最新的。net项目,SequenceEqual应该是一个不错的选择

IStructuralEquatable不是泛型的,可能导致每个比较值装箱。此外,它使用起来不是很简单,并且已经调用了一些helper方法来隐藏它。

从性能角度来看,使用如下代码可能会更好:

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

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

你可以使用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");
        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

我想确定两个集合是否有相同的内容,以任何顺序。这意味着,对于集合A中的每个元素,在两个集合中都有相同数量的具有该值的元素。我想要解释重复(所以{1,2,2,3}{1,2,3,3}不应该被认为是“相同的”)。

这是我想出的(注意,IsNullOrEmpty是另一个静态扩展方法,如果枚举对象为空或有0个元素,则返回true):

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

如果您不想比较顺序,但确实想比较每一项的计数,包括处理空值,那么我为此编写了一个扩展方法。

例如,它给出了以下结果:

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

代码如下:

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

它只枚举每个枚举对象一次,但它确实为每个枚举对象创建了一个字典,并迭代这些字典一次。我对如何改善这一点很感兴趣。

检查这个线程的答案,它将其中一个数组转换为HashSet,并使用SetEquals与另一个数组进行比较。 但是请注意,这并不检查顺序或重复

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

列表模式是在c# 11 . net 7 RC2中添加的。

int[] numbers = { 1, 2, 3 };


Console.WriteLine(numbers is [1, 2, 3]);  // True
Console.WriteLine(numbers is [1, 2, 4]);  // False