将 Linq Except 与两个 int 数组列表一起使用

Using Linq Except with two lists of int arrays

是否可以将 except 与两个 int 数组列表一起使用,如下所示:

List<int[]> a = new List<int[]>(){ new int[]{3,4,5}, new int[]{7,8,9}, new int[]{10,11,12}  };

List<int[]> b = new List<int[]>(){ new int[]{6,7,9}, new int[]{3,4,5}, new int[]{10,41,12}  };

var c = a.Except(b);

并期望 {3,4,5} 不存在于可枚举的 c 中?当然我试过了,但这个不起作用。有没有像 Except 一样高效的解决方案?或者更好、更快?

这是因为 int 数组的默认 EqualityComparer returns false 对于具有相同值的数组:

int[] a1 = { 1, 2, 3 };
int[] a2 = { 1, 2, 3 };
var ec = EqualityComparer<int[]>.Default;
Console.WriteLine(ec.Equals(a1, a2));//result is false

您可以通过实现自己的 EqualityComparer 并将其实例传递给 Except 方法 (see documentation) 来修复它。

您还可以阅读 C# 中的数组比较 here

在 .NET 中,只有当数组是完全相同的数组对象时,数组才与另一个数组相等。所以两个具有相同内容的不同数组不被认为是相等的:

int[] x = new int[] { 1, 2 };
int[] y = new int[] { 1, 2 };
Console.WriteLine(x == y); // false

为了根据内容检查是否相等,可以使用Enumerable.SequenceEqual:

Console.WriteLine(x.SequenceEqual(y)); // true

当然,当你尝试使用 Enumerable.Except 时,这并不能直接帮助你,因为默认情况下,它将使用默认的相等比较器,它只检查是否相等(并且因为每个数组都与其他数组不相等除了它自己……)。

所以解决方案是use the other overload, and provide a custom IEqualityComparer,它根据内容比较数组。

public class IntArrayEqualityComparer : IEqualityComparer<int[]>
{
    public bool Equals(int[] a, int[] b)
    {
        return a.SequenceEqual(b);
    }

    public int GetHashCode(int[] a)
    {
        return a.Sum();
    }
}

不幸的是,仅仅委派给 SequenceEqual 是不够的。我们还必须提供 GetHashCode 实现才能使其正常工作。作为一个简单的解决方案,我们可以在这里使用数组中数字的总和。通常,我们希望提供一个强大的散列函数,它可以告诉我们很多关于内容的信息,但由于我们只将这个散列函数用于 Except 调用,我们可以在这里使用一些简单的东西。 (一般来说,我们也希望避免从可变对象创建哈希值)

并且在使用相等比较器时,我们正确地过滤掉了重复的数组:

var c = a.Except(b, new IntArrayEqualityComparer());