【问题标题】:Make sure every item in list is unique?确保列表中的每个项目都是唯一的?
【发布时间】:2014-10-17 18:07:47
【问题描述】:

假设我有一个包含 1 到 10.000.000 个项目的列表。类型为列表。 CustomObj 看起来像这样:

class Person 
{
   public string Prename;
   public string Lastname;

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

我想确保此列表中的每个人都是独一无二的。因此,如果我尝试添加“Tim Stone”并且列表中已经存在“Tim Stone”,则不会添加或过滤掉新的。

我尝试使用 List.Distinct() 函数来删除重复项。可悲的是,它不适用于自定义对象,我最终得到了重复。

HashSet 可以是我要找的吗?如果是这样,实现会是什么样子?

问候

【问题讨论】:

    标签: c# list filter unique hashset


    【解决方案1】:

    您可以将它们添加到HashSet,而不是首先将它们添加到列表中。覆盖 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 的人,但您可以将其更改为您喜欢的。

    【讨论】:

    • 如果PrenameLastnameHashSet 中发生变化会怎样?根据你的GetHashCode方法,是找不到的。
    • 我认为 OP 只是关心添加或过滤列表。如果在集合中更新,则使用自定义检查重复的方式实现 IEqualityComparer 接口可能是有意义的。
    【解决方案2】:

    如果您不关心集合中元素的顺序,那么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"),请确保重载它们。

    【讨论】:

      【解决方案3】:

      如果您想对自定义对象使用HashSet&lt;T&gt; 或任何Distinct 操作,可以使自定义对象实现IEquatable 接口(遵循该页面上的所有指导,包括覆盖GetHashCode)。完成后,BCL 集合和 LINQ 操作将按照您希望的方式运行。

      您应该知道,让GetHashCode 使用可以更改的类的属性可能会导致非常糟糕的事情发生(例如,字典或集合中的项目可能会“丢失”)。如果您不能使重要属性不可变,您可以通过创建 IList&lt;T&gt; 的自定义实现来满足您的要求,该实现包装了标准 List&lt;T&gt; 并实现您的集合类型的 Add 方法,如下所示:

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

      此解决方案效率会低得多,但可能会为您节省一些令人费解的错误。

      【讨论】:

        猜你喜欢
        • 2016-09-13
        • 1970-01-01
        • 2011-04-30
        • 2023-03-17
        • 2020-12-29
        • 2021-12-18
        • 1970-01-01
        • 1970-01-01
        • 2013-08-14
        相关资源
        最近更新 更多