两个List<int[]>之间的交集

3
我想要获取以下两个列表的交集。
List<int[]> list1 = new List<int[]>
{
    new int[] { 0, 0, 0, },
    new int[] { 1, 1, 1, }
};

List<int[]> list2 = new List<int[]>
{
    new int[] { 1, 1, 1, },
    new int[] { 2, 2, 2, }
};

但是当我尝试时

List<int[]> intersection = list1.Intersect(list2).ToList();

交集返回为空。当我尝试使用两个 List<int>(不是数组)进行相同的设置时,它按预期工作。

这是我第一次使用 Linq。如何获取此交集?


5
你需要提供一个自定义的相等比较器来比较数组内容。默认的相等比较器是引用比较。 - Raymond Chen
2个回答

4

使用 Where()Any() 方法

您可以使用 SequenceEqual 方法比较继承了 IEnumerable 接口的列表/数组和每个对象的相等性。

List<int[]> intersection = list1.Where(l1 => list2.Any(l2=> l1.SequenceEqual(l2))).ToList();

1

用于检查相等性的默认比较器是引用比较
这个默认值不适合比较数组的内容。

您可以通过使用一个自定义比较器(派生自IEqualityComparer),实际上比较数组的内容来实现您需要的功能:

// Custom comparer:
class MyComparer : IEqualityComparer<int[]>
{
    public bool Equals(int[] a, int[] b) 
    {
        if (ReferenceEquals(a, b)) return true; 
        if (a is null) return false; 
        if (b is null) return false;
        return a.SequenceEqual(b); 
    }
    public int  GetHashCode(int[] a)     
    { 
        return a.Aggregate(1, (acc, item) => acc * item); 
    }
}

然后你可以用于交集:

List<int[]> list1 = new List<int[]>
            {
                new int[] { 0, 0, 0, },
                new int[] { 1, 1, 1, }
            };

List<int[]> list2 = new List<int[]>
            {
                new int[] { 1, 1, 1, },
                new int[] { 2, 2, 2, }
            };

// Intersection with custom comparer:
//------------------------------------------------vvvvvvvvvvvvvvvv----------
List<int[]> intersection = list1.Intersect(list2, new MyComparer()).ToList();

foreach (var list in intersection)
{
    Console.WriteLine("{0}", string.Join(", ", list));
}

输出:

1, 1, 1

注意:
我的GetHashCode实现只是一个简单的示例,您可能需要进行改进。
您可以参考@Oliver在下面的评论中关于如何改进它的建议(需要Microsoft.Bcl.HashCode NuGet包来使用HashCode)。
我的GetHashCode还存在一个问题,即需要遍历整个数组。当数组很长时,它也容易发生溢出。正如@Dmitry Bychenko所评论的那样,您可以使用以下代码代替(同样需要来自Microsoft.Bcl.HashCode的HashCode):

return HashCode.Combine(a.DefaultIfEmpty().Take(4)); 

这将最多考虑4个项目,并让.Net将它们组合。


改进 GetHashCode() 最简单的方法是 var hash = new HashCode(); foreach() { hash.Add(i); }; return hash.ToHashCode(); - Oliver
@Oliver 谢谢,已更新 Equals。关于 GetHashCode - 我会让 OP 自己处理,因为它需要一个 NuGet 包,这会使我的答案不是自包含的。 - wohlstad
由于列表可能会很长,因此 return a.Aggregate(1, (acc, item) => acc * item); 可能会很慢(它会聚合整个列表)。另一个问题是整数溢出:acc * item 可能会超过 int.MaxValue 并且抛出异常(这取决于项目设置)。 我建议改用 return HashCode.Combine(value.DefaultIfEmpty().Take(4));。我们最多考虑四个项,并让 .Net 结合它们。 - Dmitry Bychenko
@DmitryBychenko 说得好。已添加注释。 - wohlstad

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