【问题标题】:Should I use a concatenation of my string fields as a hash code?我应该使用我的字符串字段的串联作为哈希码吗?
【发布时间】:2009-06-05 19:09:35
【问题描述】:

我在 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; }
}

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

即:

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

return str.ToString().GetHashCode();

这样做的优点/缺点是什么?为什么我不应该这样做?

【问题讨论】:

标签: c# string hashcode equality iequatable


【解决方案1】:

我会避免这样做,因为它会毫无意义地创建一堆字符串 - 尽管 Kosi2801 关于使碰撞变得简单的观点也是相关的。 (由于字段的性质,我怀疑它实际上不会造成很多碰撞,但是...)

我会选择我以前used in this answer 的“简单且易于正确”的算法(感谢您查找 lance :) - 正如您所说,它在 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;
}

当然,这不是空安全的。如果您使用的是 C# 3,您可能需要考虑扩展方法:

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

那么你可以使用:

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;
}

可以创建一个参数数组方法实用程序来简化此操作:

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

并调用它:

public int GetHashCode()
{
    return HashHelpers.GetHashCode(StreetAddress, RuralRoute, City,
                                   Province, Country, PostalCode);
}

在大多数类型中都涉及到原语,因此会在某种程度上不必要地执行装箱,但在这种情况下,您只有引用。当然,您最终会不必要地创建一个数组,但您知道他们所说的过早优化...

【讨论】:

  • 另一种解决方案是使用 EqualityComparer.Default.GetHashCode(someValue)。这是一个空安全散列机制,从 2.0 开始就在框架中
  • 谢谢,完美。您的 GetNullSafeHashCode() 方法中有一个小错误:它缺少“this”。
  • 噢!非常感谢。固定。
  • 也可以在 GetNullSafeHashCode() 中使用它:返回值?.GetHashCode() ?? 1;
  • @KevinVictor:你可以现在,是的......但不能回到 2009 年 :)
【解决方案2】:

不要这样做,因为尽管哈希码相同,但对象可能不同。

想想

"StreetAddress" + "RuralRoute" + "City"

"Street" + "AddressRural" + "RouteCity"

两者的哈希码相同,但字段内容不同。

【讨论】:

  • 这是一个很好的观点,我什至没有考虑过。尽管在实践中似乎不太可能发生。
  • 不能通过在字段之间放置分隔符轻松解决吗? (例如"StreetAddress" + "/delimiter/" + "RuralRoute" + "/delimiter/" + "City"。在某些语言中可以是一个变量来避免多重赋值吗?)
【解决方案3】:

对于这种事情,你可能想要实现IEqualityComparer&lt;Address&gt;

public class Address : IEqualityComparer<Address>
{        
    //
    // member declarations
    //

    bool IEqualityComparer<Address>.Equals(Address x, Address y)
    {
        // implementation here
    }

    int IEqualityComparer<Address>.GetHashCode(Item obj)
    {
        // implementation here
    }
}

您也可以实现IComparable&lt;Address&gt; 来获取订单...

【讨论】:

    【解决方案4】:
    public string getfourDigitEncryptedText(string input) { 
        int hashCode = input.hashCode(); 
        string hstring = (new StringBuilder()).append(hashCode).append("").toString(); 
        string rev_hstring = (new StringBuilder(hstring)).reverse().toString(); 
        string parts[] = rev_hstring.trim().split(""); 
        int prefixint = 0; 
        for(int i = 1; i <= parts.length - 3; i++) 
            prefixint += integer.parseInt(parts[i]);   
        string prefixstr = "0"; 
        if((new integer(prefixint)).toString().length() < 2) 
            prefixstr = (new StringBuilder()).append((new integer(prefixint)).toString()).append("5").toString(); 
        else if((new integer(prefixint)).toString().length() > 2) 
            prefixstr = "79"; 
        else 
            prefixstr = (new integer(prefixint)).toString(); 
        string finalstr = (new StringBuilder()).append(prefixint).append(rev_hstring.substring(3, 5)).toString();
        return finalstr; 
    }  
    

    【讨论】:

    • 你能解释一下你的代码是做什么的吗?
    猜你喜欢
    • 1970-01-01
    • 2012-08-23
    • 2020-10-16
    • 1970-01-01
    • 1970-01-01
    • 2013-09-15
    • 2013-03-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多