我有两个对象列表的数据集,它们的ID在两个列表中都是一致的,但其他属性可能有所不同,也可能没有。如何根据一个或多个属性最有效地检索不同的?
我通常采用的方法就是这样。我的对象设置如下:
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;
}
}
使用IsEqual比较器将其与某个等效对象进行比较。
然后我找到修改过的人的方法就像:
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;
}
在我的数据集中,我并不关心listB中的人,而不关心listA,所以我不需要遍历两个列表。我只需要检查listA中listB中的元素(可能存在也可能不存在),并返回已修改的人员列表(使用listA中的元素)。
这种方法适用于相当小的列表,但现在我有两个列表,大约有160,000人,这种方法需要几分钟。有没有办法让这个方法更有效率,同时仍然返回我需要它做的事情?
答案 0 :(得分:3)
如果您可以将个人ID作为密钥更改为Dictionary<int, Person>
,那么这对您有用。这将在O(n)
而不是您的O(n^2)
中运行。
public static List<Person> FindModifiedPeople(Dictionary<int, Person> dictA, Dictionary<int, Person> dictB)
{
var modifiedPeople = new List<Person>();
foreach (var personA in dictA)
{
Person matchingPerson;
if(dictB.TryGetValue(personA.Key, out matchingPerson))
{
if (!personA.Value.IsEqual(matchingPerson))
{
modifiedPeople.Add(personA.Value);
}
}
}
return modifiedPeople;
}
您也可以根据需要将返回类型从List更改为另一个Dictionary。
修改
正如@maccettura在他的评论中指出的那样,你真的应该覆盖内置的equals方法。这会使你的代码看起来像这样。
public override bool Equals(Object obj)
{
if (obj == null || GetType() != obj.GetType())
return false;
var otherPerson = (Person)obj;
if (Name != otherPerson.Name)
{
return false;
}
if (Age != otherPerson.Age)
{
return false;
}
return true;
}
这将允许您的代码使用任何期望使用默认Equals方法而不是自定义方法的东西。
答案 1 :(得分:3)
您确定比较是瓶颈吗?我认为问题来自你在这一行中的搜索:
var matchingPerson = listB.FirstOrDefault(e => e.ID == personA.ID);
在那里,您正在使用O(n)的logartihmic复杂度进行搜索,这与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);
}
}