在集合之间查找唯一对象

Posted

技术标签:

【中文标题】在集合之间查找唯一对象【英文标题】:Finding Unique Objects between collections 【发布时间】:2021-09-24 09:14:54 【问题描述】:

我有一个测试,我创建了两个列表。一个代表我已经从某个来源(我无法控制)收集并收集的数据,另一个代表我的存储库中已经存在的已知数据。

它们看起来像这样:

var newAccounts = new[]

    new Account
    
        Id = 1,
        SA_ID = 1,
        SA_Name = "Sa_Name1",
        RelationshipType = "new",
        LE_ID = 1,
        LE_GroupID = 1,
        SiteID = 1,
        MinDate = DateTime.Now.AddDays(-1),
        MaxDate = DateTime.Now.AddDays(1),
        DaysOn = 1,
        Child1 = new List<Child1> new Child1
            
                SiteID = 1,
                MaxDate = DateTime.Today.AddDays(7),
                MinDate = DateTime.Today.AddDays(1),
            
        ,
        Child2 = new List<Child2>
        
            new Child2
            
                SA_ID = 1,
                LastUpdate = DateTime.Now.AddDays(-1),
                CommentText = "Account added",
                Status = AccountStatus.AccountAdded.ToString(),
            
        
    ,
    new Account
    
        Id = 2,
        SA_ID = 2,
        SA_Name = "Sa_Name2",
        RelationshipType = "new",
        LE_ID = 2,
        LE_GroupID = 2,
        SiteID = 2,
        MinDate = DateTime.Now.AddDays(-2),
        MaxDate = DateTime.Now.AddDays(2),
        DaysOn = 2,
    ,
    new Account
    
        Id = 3,
        SA_ID = 3,
        SA_Name = "Sa_Name3",
        RelationshipType = "new",
        LE_ID = 3,
        LE_GroupID = 3,
        SiteID = 3,
        MinDate = DateTime.Now.AddDays(-3),
        MaxDate = DateTime.Now.AddDays(3),
        DaysOn = 3,
    
;

var knownAccounts = new[]

    new Account
    
        Id = 1,
        SA_ID = 1,
        SA_Name = "Sa_Name1",
        RelationshipType = "new",
        LE_ID = 1,
        LE_GroupID = 1,
        SiteID = 1,
        MinDate = DateTime.Now.AddDays(-1),
        MaxDate = DateTime.Now.AddDays(1),
        DaysOn = 1,
        Child1 = new List<Child1> new Child1
            
                SiteID = 1,
                MaxDate = DateTime.Today.AddDays(7),
                MinDate = DateTime.Today.AddDays(1),
            
        ,
        Child2 = new List<Child2>
        
            new Child2
            
                SA_ID = 1,
                LastUpdate = DateTime.Now.AddDays(-1),
                CommentText = "Account added",
                Status = AccountStatus.AccountAdded.ToString(),
            
        
    
;

在我的单元测试中,我想从newAccounts 中删除Account ID 1,所以我的收藏中只剩下2 个条目。这些是我迄今为止的尝试:

public List<T> ReturnUniqueEntriesList<T>(List<T> newAccounts, List<T> knownAccounts)

    var a = knownAccounts.Intersect(newAccounts).ToList();

    var listA = newAccounts.Except(knownAccounts).ToList();
    var listB = knownAccounts.Except(newAccounts).ToList();

    var result = listB.Intersect(listA).ToList();

    return result;
  

当我运行它时,最终结果为 0。a 也返回 0,listAlistB 只需返回它们各自的对象。

我在这里做错了什么/错过了什么?任何帮助将不胜感激

【问题讨论】:

对于相交/排除等操作,列表中的对象需要具有可比性。如果不实现(IComparable、IEquatable 等)任何内容,将使用内存地址。所以 knownAccount 和 newAccount 中的实例总是不同的,因为它们是不同的实例,即使它们包含相同的数据。 【参考方案1】:

为 Account 覆盖 Equals 和 GetHashcode,使它们不依赖于默认实现(对象的内存地址)。这意味着 C# 将能够在执行例外时正确地将它们等同起来。

例如:

public class Account

    public override bool Equals(object other)
      return other is Account a && a.Id == this.Id; //nb; is returns false if other is a null, even if it is an Account
    

    public override int GetHashCode()
      return Id.GetHashCode();
    

事实上,以下两个帐户非常不同:

var a = new Account  Id = 1 ;
var b = new Account  Id = 1 ;

..因为它们位于不同的内存地址。

通过重写 Equals 使其比较另一个的 Id,而不管其他属性,然后您基本上可以实现您似乎描述的“具有相同 ID 的两个帐户对象是等效的”的情况

如果其他属性因素影响决策,也添加这些属性。 Hashcode.Combine 是一种有用的方法,可以将多个哈希码组合起来,以解决获取多个属性的哈希码以产生合适的新信号哈希码的难题 - https://docs.microsoft.com/en-us/dotnet/api/system.hashcode.combine?view=net-5.0

【讨论】:

是的,Enumerable.Except Method 的备注部分说 “默认相等比较器 Default,用于比较类型的值。要比较自定义数据类型,您需要覆盖Equals 和 GetHashCode 方法,并可选择在自定义类型中实现 IEquatable 泛型接口。".

以上是关于在集合之间查找唯一对象的主要内容,如果未能解决你的问题,请参考以下文章

Java List集合

Java List集合

集合框架

查找 3 个 C++ 中的 1 个集合唯一的单词

使用java实现面向对象 第六章

Java学习集合