确保列表中的每个项目都是唯一的

本文关键字:唯一 项目 列表 确保 | 更新日期: 2023-09-27 18:19:12

假设我有一个1到10,000.000项的列表。类型为List。CustomObj看起来像这样:

class Person 
{
   public string Prename;
   public string Lastname;
   public CustomObj(string pre, string last)
   {
      Prename = pre; 
      Lastname = last;
   }
}

我想确定,这个名单上的每个人都是独一无二的。因此,如果我尝试添加一个"Tim Stone",而列表中已经有一个"Tim Stone",则不会添加或过滤掉新的"Tim Stone"。

我尝试使用List.Distinct()函数来删除重复项。遗憾的是,它不能很好地处理自定义对象,我最终得到了重复的对象。

可以一个哈希集,我正在寻找什么?如果是这样,实现会是什么样子?

确保列表中的每个项目都是唯一的

您可以将它们添加到您提到的HashSet中,而不是首先将它们添加到List中。覆盖EqualsGetHashCode方法。例如,您可以这样做

public class Person  
{
    public string Prename;
    public string Lastname;

    public Person(string pre, string last)
    {
        Prename = pre; Lastname = last;
    }
    public override bool Equals(object obj)
    {
        Person p = obj as Person;
        //can make this check case insensitive using the overload
        return (Prename + Lastname).Equals(p.Prename + p.Lastname);
    }
    public override int GetHashCode()
    {
        return (Prename + Lastname).GetHashCode();
    }
}

这样,当您将它们添加到HashSet时,将不会添加重复项。如果你已经有了一个列表,你可以像这样使用HashSet's构造函数重载:

HashSet<Person> hsPerson = new HashSet<Person>(myExistingList);

你最终会得到一个HashSetPerson对象,它不会有重复的。

我上面的实现假设一个重复的人有相同的prenamelastname一旦他们被连接,但你可以改变它到你喜欢的

如果您不关心集合中元素的顺序,那么HashSet就是您的选择。

它的方法几乎与List相同,因为它们实现了ICollectionIEnumerable等通用接口。下面是一个示例:

HashSet<Person> people = new HashSet<Person>();
var heko = new Person("heko", "17");
people.Add(heko); // people now contains heko
people.Add(heko); // people still contains only heko since duplicates are not allowed
people.Add(new Person("Nikola", "Dimitroff")); // people contains heko and nikola

有几件事需要注意。首先,由于HashSet没有保持元素的顺序,你不能通过它们的索引来获取元素,即people[0]是无效的操作。要枚举集合中的人员,请使用foreach

其次,HashSet在比较项时使用==运算符和GetHashCode方法。如果您认为new Person("heko", 17") == new Person("heko", "17") .

如果您想使用HashSet<T>或任何Distinct操作与您的自定义对象,您可以使您的自定义对象实现等价接口(遵循该页上的所有指导,包括覆盖GetHashCode)。一旦这样做了,BCL集合和LINQ操作将按照您希望的方式运行。

你应该意识到,让GetHashCode使用类的属性,这些属性可以被更改,可能会导致非常糟糕的事情发生(例如,字典或集合中的项可能会"丢失")。如果你不能让你的重要属性不可变,你可以通过创建一个自定义的IList<T>实现来满足你的要求,它包装了一个标准的List<T>,并实现了Add方法的集合类型,像这样:

public void Add(Person person)
{
   if (!_list.Any(p => p.Prename == person.PreName && p.Lastname == person.Lastname))
   {
      _list.Add(person);
   }
}

这个解决方案的效率要低得多,但可能会为您节省一些令人费解的bug。