我有一个模型课:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
当我添加两个列表时:
List<Person> people1 = new List<Person> {
new Person() { Id = 1, Name = "Name1" },
new Person() { Id = 2, Name = "Name2" },
new Person() { Id = 3, Name = "Name3" },
};
List<Person> people2 = new List<Person> {
new Person() { Id = 1, Name = "Name1" },
new Person() { Id = 4, Name = "Name4" },
new Person() { Id = 5, Name = "Name5" },
};
people1.AddRange(people2);
如果person
中的people2
在id
中的person
中具有相同的people1
,则我不想添加它。我该怎么办?
答案 0 :(得分:10)
您可以对此使用LINQ相当容易但效率低下:
people1.AddRange(people2.Where(p2 => !people1.Any(p1 => p1.Id == p2.Id)));
或者您可以先创建一组ID:
HashSet<int> people1Ids = new HashSet<int>(people1.Select(p1 => p1.Id));
people1.AddRange(people2.Where(p2 => !people1Ids.Contains(p2.id));
第一种方法显然更简单,但是如果列表很大,它可能会变慢,因为对于people2
中的 every 元素,它将遍历 every < / em> people1
中的元素。
如果people1
很大,第二种方法将大大加快 。如果people2
很大,您将不会获得太大收益。例如,如果people1
仅包含几个人,则在哈希集中查找ID不会比遍历列表快得多。
您可以采取完全不同的方法。如果您根据ID使Person
类型的实现IEquatable<Person>
-或创建一个这样做的IEqualityComparer<Person>
,并且 if 则不需要那么多现有的要修改的列表,只要您需要“两个列表的并集”,而 if 则不必关心顺序,而 if 中的所有条目每个列表都是唯一的,或者您不介意删除重复列表,您可以使用:
// Specify the comparer as another argument if you need to.
// You could call ToList on the result if you need a list.
var union = people1.Union(people2);
(该解决方案有很多条件,但可能都有效)。
答案 1 :(得分:3)
您可以将Union运算符与自定义IEqualityComparer
配合使用。这将创建一个新列表,该列表是其他2的组合。实现客户IEqualityComparer可让您控制组成同一记录的内容。
var allPeople = people1.Union(people2, new PersonComparer());
public class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
// ommited null checks etc
return x.Id == y.Id;
}
public int GetHashCode(Person obj)
{
// ommited null checks etc
obj.Id.GetHashCode()
}
}
答案 2 :(得分:2)
使用此:
people1.AddRange(people2.Except(people1));
但是您首先需要在Person类中重写Equal和GetHashCode:
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
if (!(obj is Person))
return false;
Person p = (Person)obj;
return (p.Id == Id && p.Name == Name);
}
public override int GetHashCode()
{
return String.Format("{0}|{1}", Id, Name).GetHashCode();
}
}
或者您可以使用自定义的相等比较器,然后使用Distinct(new DistinctItemComparer())
:
public class DistinctItemComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
return x.Id == y.Id &&
x.Name == y.Name;
}
public int GetHashCode(Person obj)
{
return obj.Id.GetHashCode() ^
obj.Name.GetHashCode();
}
}
然后像这样使用它:
people1.AddRange(people2.Except(people1, new DistinctItemComparer()));
如果您只需要基于Id
进行区分,则可以从这两种方法中排除Name
。
基于this,第二种方法似乎更好,因为Microsoft已经建议不要重载引用类型上的运算符等于。
答案 3 :(得分:1)
我认为这里的最佳解决方案是使用LINQ。它写的代码非常简单和简短:
people1.AddRange(people2.Where(x => !people1.Any(y => x.Id == y.Id)));
答案 4 :(得分:1)
您是否考虑过改用Dictionary
?
您可以将Id
用作Key
,它不允许重复的密钥存在吗?
以下代码是我以前使用的方式:
var dictionary = people1.ToDictionary(x => x.Id, x => x);
foreach(var person in people2)
{
if(!dictionary.ContainsKey(item.Id))
{
dictionary.Add(item.Id, item);
}
}
也许有更好的方法,但这对我有用。
这样,当您向字典中添加项目时,就不会再添加具有相同Id
的内容。
也检查HashSets
,因为他们做类似的事情。