如何有效地比较C#中两个大型对象列表的属性?
我有一个包含两个对象列表的数据集,它的ID在两个列表中都是一致的,但其他属性可能不同,也可能不同。如何最有效地检索基于一个或多个属性的不同属性 我通常的做法是这样的。我的对象设置如下:如何有效地比较C#中两个大型对象列表的属性?,c#,C#,我有一个包含两个对象列表的数据集,它的ID在两个列表中都是一致的,但其他属性可能不同,也可能不同。如何最有效地检索基于一个或多个属性的不同属性 我通常的做法是这样的。我的对象设置如下: public class Person { public int ID { get; set; } public string Name { get; set; } public int Age { get; set;
public class Person
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
public bool IsEqual(Person other)
{
if (Name != other.Name)
{
return false;
}
if (Age != other.Age)
{
return false;
}
return true;
}
}
其中,使用等质量比较器将其与某个等效对象进行比较
然后我找到被改造人的方法是:
public static List<Person> FindModifiedPeople(List<Person> listA, List<Person> listB)
{
var modifiedPeople = new List<Person>();
foreach (var personA in listA)
{
var matchingPerson = listB.FirstOrDefault(e => e.ID == personA.ID);
if (matchingPerson == null)
{
continue;
}
if (!personA.IsEqual(matchingPerson))
{
modifiedPeople.Add(personA);
}
}
return modifiedPeople;
}
公共静态列表FindModifiedPeople(列表A、列表B)
{
var modifiedPeople=新列表();
foreach(listA中的var personA)
{
var matchingPerson=listB.FirstOrDefault(e=>e.ID==personA.ID);
if(matchingPerson==null)
{
继续;
}
如果(!personA.IsEqual(匹配人))
{
修改人物。添加(人物角色);
}
}
返回修改人;
}
在我的数据集中,我不关心listB中的人,但不关心listA中的人,所以我不需要遍历这两个列表。我只需要在listB中检查listA中的元素(可能存在也可能不存在),并返回一个已修改的人员列表(使用listA中的元素)
这种方法适用于相当小的列表,但现在我有两个约160000人的列表,这种方法需要几分钟。有没有办法让这个方法更有效,同时还返回我需要它做的事情 如果你可以将你的列表更改为一本
字典
,并将个人ID作为关键,那么这对你很有用。这将在O(n)
中运行,而不是在O(n^2)
中运行
这将允许您的代码处理任何希望使用默认Equals方法而不是自定义方法的内容。您确定比较是瓶颈吗?我认为问题来自您在这一行中所做的搜索:
var matchingPerson = listB.FirstOrDefault(e => e.ID == personA.ID);
在这里,您正在执行一个日志复杂度为O(n)的搜索,它与foreach
循环相结合,给出了O(n^2)的总复杂度。相反,您可以预先创建一个字典,这需要一些时间,但查找速度要快得多。字典应该以ID作为键,并且可以在foreach
循环之前轻松创建:
var dictB = listB.ToDictionary(p => p.ID);
之后,您的查找速度会快得多,如下所示:
Person matchingPerson;
if (dictB.TryGetValue(personA.ID, out matchingPerson))
{
if (!personA.IsEqual(matchingPerson))
{
modifiedPeople.Add(personA);
}
}
你必须使用列表吗?如果你有这个人的身份证,你能不能把他们储存在字典之类的东西里?你真的需要把这些名单放在一起比较吗?如果对象已更新,为什么不在对象内跟踪并将其作为布尔属性公开(即`person.IsDirty`)?尽管我现在意识到
person.IsDirty
可能不是最好的命名约定…而且,不要在这里重新发明轮子,重写而不是编写自己的IsEqual()
注意,您的“通常”处理对象集的方法非常不寻常。。。列表在几乎所有的集合操作中都非常糟糕-请参阅以获取指导…我认为最好也提到重写Equals()
(最好给OP一些更好的实践)是的。我什么都没说只是因为你已经把它放在你的评论里了,但我会把它加在这里。哇!这是一个巨大的不同!太棒了,谢谢你。还有,是的,我知道我应该重写Equals()。我的同事已经为Equals添加了一个覆盖,因为他只想在比较id时返回true,而不关心属性。所以他的重写类似于:public override bool Equals(Person other){if(ID!=other.ID){return false;}return true;}显然,在我的例子中,我希望Equals专门比较属性。我真的不知道如何解决这个难题,所以我把我的方法作为一个单独的方法。你完全正确,第一个默认值确实是瓶颈,而不是比较器。我已经将其更改为从列表中生成一个字典(我确实需要该列表用于其他目的),甚至包括开销,查找速度也显著加快。如果可以的话,我会把你和迈克尔·夏普都列为公认的答案!
var dictB = listB.ToDictionary(p => p.ID);
Person matchingPerson;
if (dictB.TryGetValue(personA.ID, out matchingPerson))
{
if (!personA.IsEqual(matchingPerson))
{
modifiedPeople.Add(personA);
}
}