从列表C#中删除重复项

San*_*ndy 7 c# list

我正在关注stackoverflow上的一篇关于从C#中的List中删除重复项的帖子.

如果<T>是某些用户定义类型,如:

class Contact
{
  public string firstname;
  public string lastname;
  public string phonenum;
}
Run Code Online (Sandbox Code Playgroud)

建议的(HashMap)不会删除重复.我想,我必须重新定义一些比较两个对象的方法,不是吗?

Jon*_*eet 20

A HashSet<T> 确实删除重复项,因为它是一个集合...但仅当您的类型适当地定义相等时.

我怀疑是"复制"你的意思是"具有相同的字段值到另一个对象的对象" -你需要重写Equals/ GetHashCode对于工作,和/或实现IEquatable<Contact>...或者你可以提供一个IEqualityComparer<Contact>HashSet<T>构造函数.

而不是使用HashSet<T>可以只调用DistinctLINQ扩展方法.例如:

list = list.Distinct().ToList();
Run Code Online (Sandbox Code Playgroud)

但同样,你需要以某种方式或其他方式提供适当的平等定义.

这是一个示例实现.注意我是如何使它变为不可变的(对于可变类型,相等是奇数,因为两个对象可以等于一分钟而下一个不相等)并且使用公共属性使字段成为私有.最后,我已经密封了类 - 通常应该密封不可变类型,这使得更容易讨论平等.

using System;
using System.Collections.Generic; 

public sealed class Contact : IEquatable<Contact>
{
    private readonly string firstName;
    public string FirstName { get { return firstName; } }

    private readonly string lastName;
    public string LastName { get { return lastName; } }

    private readonly string phoneNumber;
    public string PhoneNumber { get { return phoneNumber; } }

    public Contact(string firstName, string lastName, string phoneNumber)
    {
        this.firstName = firstName;
        this.lastName = lastName;
        this.phoneNumber = phoneNumber;
    }

    public override bool Equals(object other)
    {
        return Equals(other as Contact);
    }

    public bool Equals(Contact other)
    {
        if (object.ReferenceEquals(other, null))
        {
            return false;
        }
        if (object.ReferenceEquals(other, this))
        {
            return true;
        }
        return FirstName == other.FirstName &&
               LastName == other.LastName &&
               PhoneNumber == other.PhoneNumber;
    }

    public override int GetHashCode()
    {
        // Note: *not* StringComparer; EqualityComparer<T>
        // copes with null; StringComparer doesn't.
        var comparer = EqualityComparer<string>.Default;

        // Unchecked to allow overflow, which is fine
        unchecked
        {
            int hash = 17;
            hash = hash * 31 + comparer.GetHashCode(FirstName);
            hash = hash * 31 + comparer.GetHashCode(LastName);
            hash = hash * 31 + comparer.GetHashCode(PhoneNumber);
            return hash;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:好的,响应请求解释GetHashCode()实现:

  • 我们想要组合此对象的属性的哈希码
  • 我们没有在任何地方检查无效,所以我们应该假设它们中的一些可能为空.EqualityComparer<T>.Default总是处理这个,这很好......所以我用它来获取每个字段的哈希码.
  • 将几个哈希码组合成一个的"加和乘"方法是Josh Bloch推荐的标准方法.还有很多其他的通用哈希算法,但是这个算法适用于大多数应用程序.
  • 我不知道你是否默认在一个已检查的上下文中编译,所以我把计算放在一个未经检查的上下文中.我们真的不在乎重复的乘法/加法是否会导致溢出,因为我们不是在寻找"幅度"......只是我们可以为相同的对象重复到达的数字.

顺便说一句,有两种处理无效的替代方法:

public override int GetHashCode()
{
    // Unchecked to allow overflow, which is fine
    unchecked
    {
        int hash = 17;
        hash = hash * 31 + (FirstName ?? "").GetHashCode();
        hash = hash * 31 + (LastName ?? "").GetHashCode();
        hash = hash * 31 + (PhoneNumber ?? "").GetHashCode();
        return hash;
    }
}
Run Code Online (Sandbox Code Playgroud)

要么

public override int GetHashCode()
{
    // Unchecked to allow overflow, which is fine
    unchecked
    {
        int hash = 17;
        hash = hash * 31 + (FirstName == null ? 0 : FirstName.GetHashCode());
        hash = hash * 31 + (LastName == null ? 0 : LastName.GetHashCode());
        hash = hash * 31 + (PhoneNumber == null ? 0 : PhoneNumber.GetHashCode());
        return hash;
    }
}
Run Code Online (Sandbox Code Playgroud)