在LINQ中使用Union合并列表时删除重复项

本文关键字:删除 列表 合并 LINQ Union | 更新日期: 2023-09-27 18:17:17

我试图在LinqPad中使用list.Union合并两个列表,但我无法使其工作,并想检查我的理解是否正确。

给定这个简单的类:

public class Test 
{
   public int Id { get; set;}
   public int field1 { get; set; }
   public bool Equals(Test other)
   {        
      return this.Id.Equals(other.Id);
   }
}

和像这样填充的两个列表:

List<Test> list = new List<Test>();
list.Add( new Test { Id = 1, field1 = 1});
list.Add( new Test { Id = 1, field1 = 2});
list.Add( new Test { Id = 2, field1 = 3});
list.Add( new Test { Id = 2, field1 = 4});
List<Test> list2 = new List<Test>();
list2.Add( new Test { Id = 1, field1 = 1});
list2.Add( new Test { Id = 1, field1 = 2});
list2.Add( new Test { Id = 2, field1 = 3});
list2.Add( new Test { Id = 2, field1 = 4});

然后尝试:var mergedList = list.Union(list2).ToList();并使用简单的foreach循环输出数据,并获得此输出:

ID: 1 -------- 1
ID: 1 -------- 2
ID: 2 -------- 3
ID: 2 -------- 4
ID: 1 -------- 1
ID: 1 -------- 2
ID: 2 -------- 3
ID: 2 -------- 4

我的印象是Union应该删除重复项以返回:

ID: 1 -------- 1
ID: 1 -------- 2
ID: 2 -------- 3
ID: 2 -------- 4

是我做错了什么还是我误解了?

此外,它应该工作没有显式覆盖Test类中的Equals方法?

谢谢

在LINQ中使用Union合并列表时删除重复项

在您的例子中,您只是定义了一些LINQ一无所知的方法。这就像创建方法bool HeyEquateMeWith(Test other),并期望LINQ在执行set操作时调用它。

你需要定义你的类如下(覆盖 ObjectEqualsGetHashCode方法):

public class Test 
{
   public int Id { get; set;}
   public int field1 { get; set; }  
   public override bool Equals(object other) //note parameter is of type object
   {        
        Test t = other as Test;
        return (t != null) ? Id.Equals(t.Id) : false;
   }
   public override int GetHashCode()
   {
        return Id.GetHashCode();
   }
}

现在Union将调用您覆盖的EqualsGetHashCode方法。另外,当你覆盖Equals方法时,你应该总是覆盖GetHashCode

如果对默认比较器不满意,可以尝试这样做(反过来,它利用了@IlyaIvanov提到的GetHashCode方法):

// get all items that "other than in first list", so Where() and Any() are our filtering expressions
var delta = list2.Where(x2 => !list.Any(x1 => (x1.Id == x2.Id) && (x1.field1 == x2.field1)));
// now let merge two enumerables that have nothing "equal" between them
var merged = list.Union(delta).ToList();

您可以创建一个实现

的类
IEqualityComparer<Test>

这个类定义了Equals和GetHashCode之后,您可以将此比较器传递给Union方法就像这样:

public class MyComparer:IEqualityComparer<Test>{
//Equals and GetHashCode
}
var mergedList = list.Union(list2, new MyComparer()).ToList();

我想把这个留在这里给那些仍然不明白的人。我发现这篇文章对继承自IEqualityComparer的compare类非常有帮助http://alicebobandmallory.com/articles/2012/10/18/merge-collections-without-duplicates-in-c