it-swarm.cn

当两个字符串都可以互换时,如何为具有两个字符串的结构实现GetHashCode

我在C#中有一个结构:

public struct UserInfo
{
   public string str1
   {
     get;
     set;
   }

   public string str2
   {
     get;
     set;
   }   
}

唯一的规则就是UserInfo(str1="AA", str2="BB").Equals(UserInfo(str1="BB", str2="AA"))

如何覆盖此结构的GetHashCode函数?

66
Graviton

_ msdn _

哈希函数必须具有以下属性:

  • 如果两个对象比较相等,则每个对象的GetHashCode方法必须返回相同的值。但是,如果两个对象的比较不相等,则两个对象的GetHashCode方法不必返回不同的值。
  • 只要没有对对象状态的修改来确定对象的GetHashCode方法的返回值,对象的Equals方法必须始终返回相同的哈希代码。请注意,这仅适用于当前应用程序的执行,并且如果再次运行应用程序,则可以返回不同的哈希代码。
  • 为获得最佳性能,哈希函数必须为所有输入生成随机分布。

考虑到正确的方法是:

return str1.GetHashCode() ^ str2.GetHashCode() 

^可以用其他可交换操作代替

65
aku

请参阅 Jon Skeet的回答 - 像^这样的二进制操作不好,它们经常会产生碰撞哈希!

25
Tomáš Kafka
public override int GetHashCode()
{
    unchecked
    {
        return (str1 ?? String.Empty).GetHashCode() +
            (str2 ?? String.Empty).GetHashCode();
    }
}

使用'+'运算符可能比使用'^'更好,因为虽然你明确地想要('AA','BB')和('BB','AA')显然是相同的,你可能不想要( 'AA','AA')和('BB','BB')是相同的(或者所有相等的对)。

在这个解决方案中并没有完全遵守'尽快'规则,因为在空值的情况下,这对空字符串执行'GetHashCode()'而不是立即返回已知常量,但即使没有明确测量我也愿意除非你期望很多空值,否则可能会产生一种猜测,即差异不会太大而无法担心。

15
Jerry
  1. 作为一般规则,为类生成哈希码的一种简单方法是XOR所有可以参与生成哈希码的数据字段(小心检查其他人指出的null)。这也满足了UserInfo(“AA”,“BB”)和UserInfo(“BB”,“AA”)的哈希码相同的(人为的)要求。

  2. 如果您可以对类的使用做出假设,则可以改进哈希函数。例如,如果str1和str2相同,则XOR可能不是一个好的选择。但是如果str1和str2代表名字和姓氏,XOR可能是一个不错的选择。

虽然这显然不是一个真实的例子,但值得指出: - 这可能是使用结构的一个不好的例子:结构通常应该具有值语义,这似乎不是这里的情况。 - 使用带有setter的属性来生成哈希码也是一个问题。

5
Joe

一个简单的 通用 方法是这样做:

return string.Format("{0}/{1}", str1, str2).GetHashCode();

除非你有严格的性能要求,否则这是我能想到的最容易的,当我需要复合键时,我经常使用这种方法。它处理null的情况很好,并且不会导致(m)任何散列冲突(通常)。如果您希望字符串中包含“/”,只需选择另一个您不期望的分隔符。

4
Daniel Lidström

按照ReSharper的建议:

public int GetHashCode()
{
    unchecked
    {
        int hashCode;

        // String properties
        hashCode = (hashCode * 397) ^ (str1!= null ? str1.GetHashCode() : 0);
        hashCode = (hashCode * 397) ^ (str2!= null ? str1.GetHashCode() : 0);

        // int properties
        hashCode = (hashCode * 397) ^ intProperty;
        return hashCode;
    }
}

397是一个足够大的素数,可以使结果变量溢出并稍微混合散列的位,从而提供更好的散列码分布。否则397中没有什么特别的东西可以将它与其他相同数量的素数区分开来。

3
Jani Hyytiäinen
public override int GetHashCode()   
{       
    unchecked      
    {           
        return(str1 != null ? str1.GetHashCode() : 0) ^ (str2 != null ? str2.GetHashCode() : 0);       
    }   
}
3
user11556

是的,正如Gary Shutler指出的那样:

return str1.GetHashCode() + str2.GetHashCode();

可以溢出。您可以尝试使用Artem建议的时间,或者您可以在未经检查的关键字中包围该语句:

return unchecked(str1.GetHashCode() + str2.GetHashCode());
2
Grokys

试试这个:

(((long)str1.GetHashCode()) + ((long)str2.GetHashCode())).GetHashCode()
1
Artem Tikhomirov

很多可能性。例如。

return str1.GetHashCode() ^ str1.GetHashCode()

0
VolkerK

也许像str1.GetHashCode()+ str2.GetHashCode()?或(str1.GetHashCode()+ str2.GetHashCode())/ 2?无论str1和str2是否被交换,这种方式都是一样的....

0
Mike Stone

对它们进行排序,然后将它们连接起来:

 return((str1.CompareTo(str2)<1)?str1 + str2:str2 + str1)
。GetHashCode(); 
0
Steve Morgan

GetHashCode的结果应该是:

  1. 尽可能快地。
  2. 尽可能独特。

考虑到这些,我会用这样的东西:

if (str1 == null)
    if (str2 == null)
        return 0;
    else
       return str2.GetHashCode();
else
    if (str2 == null)
        return str1.GetHashCode();
    else
       return ((ulong)str1.GetHashCode() | ((ulong)str2.GetHashCode() << 32)).GetHashCode();

编辑: 忘记空值。代码已修复。

0
Omer van Kloeten