我应该使用字符串字段的串联作为哈希码吗?

cdm*_*kay 14 c# string equality hashcode iequatable

我在C#中有一个Address类,如下所示:

public class Address
{            
    public string StreetAddress { get; set; }
    public string RuralRoute { get; set; }
    public string City { get; set; }
    public string Province { get; set; }
    public string Country { get; set; }
    public string PostalCode { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我正在实现相等性,所以我需要覆盖哈希码.起初我打算使用EJ的哈希码公式,但后来我想:这些都是字符串字段,我不能只使用StringBuilder连接它们并从该字符串返回哈希码?

那是:

var str = new StringBuilder();
str.Append(StreetAddress)
   .Append(RuralRoute)
   ...

return str.ToString().GetHashCode();
Run Code Online (Sandbox Code Playgroud)

这有什么优点/缺点?我为什么不这样做?

Jon*_*eet 15

我会避免这样做只是因为它毫无意义地创造了一堆字符串 - 尽管Kosi2801关于简化碰撞的观点也很重要.(我怀疑它实际上不会产生很多碰撞,因为这些领域的性质,但......)

我会选择我以前在这个答案中使用的"简单易用"算法(感谢查找它) - 并且正如您所说,它列在Effective Java中.在这种情况下,它最终会:

public int GetHashCode()
{
    int hash = 17;
    // Suitable nullity checks etc, of course :)
    hash = hash * 23 + StreetAddress.GetHashCode();
    hash = hash * 23 + RuralRoute.GetHashCode();
    hash = hash * 23 + City.GetHashCode();
    hash = hash * 23 + Province.GetHashCode();
    hash = hash * 23 + Country.GetHashCode();
    hash = hash * 23 + PostalCode.GetHashCode();
    return hash;
}
Run Code Online (Sandbox Code Playgroud)

当然,这不是无效的.如果您使用的是C#3,则可能需要考虑扩展方法:

public static int GetNullSafeHashCode<T>(this T value) where T : class
{
    return value == null ? 1 : value.GetHashCode();
}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用:

public int GetHashCode()
{
    int hash = 17;
    // Suitable nullity checks etc, of course :)
    hash = hash * 23 + StreetAddress.GetNullSafeHashCode();
    hash = hash * 23 + RuralRoute.GetNullSafeHashCode();
    hash = hash * 23 + City.GetNullSafeHashCode();
    hash = hash * 23 + Province.GetNullSafeHashCode();
    hash = hash * 23 + Country.GetNullSafeHashCode();
    hash = hash * 23 + PostalCode.GetNullSafeHashCode();
    return hash;
}
Run Code Online (Sandbox Code Playgroud)

可以创建一个参数数组方法实用程序,以使其更简单:

public static int GetHashCode(params object[] values)
{
    int hash = 17;
    foreach (object value in values)
    {
        hash = hash * 23 + value.GetNullSafeHashCode();
    }
    return hash;
}
Run Code Online (Sandbox Code Playgroud)

并称之为:

public int GetHashCode()
{
    return HashHelpers.GetHashCode(StreetAddress, RuralRoute, City,
                                   Province, Country, PostalCode);
}
Run Code Online (Sandbox Code Playgroud)

在大多数类型中都涉及到原语,所以这会不必要地执行装箱,但在这种情况下你只有引用.当然,你最终会不必要地创建一个数组,但是你知道他们对过早优化的看法......


Kos*_*801 5

不要这样做,因为对象可以是不同的,尽管哈希码是相同的.

考虑到

"StreetAddress" + "RuralRoute" + "City"
Run Code Online (Sandbox Code Playgroud)

VS

"Street" + "AddressRural" + "RouteCity"
Run Code Online (Sandbox Code Playgroud)

两者都将具有相同的哈希码,但字段中的内容不同.