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


70

我在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函数?



3
@nawfal,不是吗?我的问题发布于08年9月16日,但您提出的问题发布于9月22/08日。
Graviton 2014年

Answers:


69

MSDN

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

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

考虑到正确的方法是:

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

^ 可以用其他换向运算代替


不应该返回str1.GetHashCode()^ str2.GetHashCode();
roomaroo

3
另外,不考虑空值。
Omer van Kloeten

15
Omer van Kloeten,对于任何.net开发人员来说都是显而易见的。快速样本旨在显示总体思路,而不是完整的解决方案
aku

2
如果您希望哈希中经常出现str1,str2和str2,str1,那么查找速度可能会比应该慢一些。也可以通过缓存哈希码来提高查找速度。显然,这些可能是过早的优化。
布赖恩2009年

+1指出使用可交换运算的重要性
Pandincus,2009年


15
public override int GetHashCode()
{
    unchecked
    {
        return (str1 ?? String.Empty).GetHashCode() +
            (str2 ?? String.Empty).GetHashCode();
    }
}

使用'+'运算符可能比使用'^'更好,因为尽管您明确希望('AA','BB')和('BB','AA')明确相同,但您可能不希望( 'AA','AA')和('BB','BB')相同(或与此完全相同的对)。

在此解决方案中并未完全遵守“尽可能快”的规则,因为在为null的情况下,它将对空字符串执行“ GetHashCode()”,而不是立即返回已知常量,但是即使没有显式测量,我也愿意冒一个猜想,除非您期望很多null,否则差异将不足以担心。


5
  1. 通常,生成类的哈希码的一种简单方法是对可以参与生成哈希码的所有数据字段进行XOR(小心检查其他人指出的null)。这也满足(人工?)要求,即UserInfo(“ AA”,“ BB”)和UserInfo(“ BB”,“ AA”)的哈希码相同。

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

尽管这显然不是一个真实的示例,但可能值得指出:-这可能是使用结构的一个较差的示例:一个结构通常应该具有值语义,而这似乎并不是这里的情况。-使用带有setter的属性来生成哈希码也很麻烦。


嗯,为什么您认为他的结构没有价值语义?您能扩展最后一句话吗?
Stefan Monov 2010年

4

一种简单的通用方法是:

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

除非您有严格的性能要求,否则这是我能想到的最简单的方法,并且在需要组合键时经常使用此方法。它null可以很好地处理各种情况,并且不会(一般)引起任何哈希冲突。如果您希望字符串中包含“ /”,则只需选择一个您不需要的分隔符即可。


确实很简单。这可以在C#6.0中简化为just return $"{str1}/{str2}".GetHashCode();。参见字符串插值
styfle '16

不安全,如果str1 =“ a / b”和str2 =“”怎么办?这将具有与str1 =“ a”和str2 =“ b /”相同的哈希值。
欧文·梅耶

1
@ErwinMayer使用您知道不在字符串中的分隔符。此外,不需要GetHashCode总是返回唯一值。它用作优化以避免重复调用Equals(精确比较通常更昂贵)。
DanielLidström'16

如何确保它为str1 =“ a”,str2 =“ b”和str1 =“ b” str2 =“ a”产生相同的哈希码?有某种魔术使“ a / b”和“ b / a”产生相同的哈希值吗?
卡斯珀·范·登·伯格

@KaspervandenBerg这两个人没有相同的哈希,因为它们不一样,对吗?
DanielLidström'17

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

7
为什么不勾选?xor不会溢出。
Konrad Rudolph

3

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中没有什么特别之处可以将其与相同大小的其他素数区分开。


该哈希码不满足OP的要求:唯一的规则是UserInfo(str1 =“ AA”,str2 =“ BB”)。Equals(UserInfo(str1 =“ BB”,str2 =“ AA”))
卡巴斯尔·范登

2

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

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

会溢出。您可以尝试按Artem的建议进行强制转换,也可以将语句放在unchecked关键字中:

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


0

许多可能性。例如

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


0

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


0

对它们进行排序,然后将它们串联:

return((str1.CompareTo(str2)<1)?str1 + str2:str2 + str1)
    .GetHashCode();

2
这将导致您的GetHashCode方法执行大量工作。哈希码旨在快速实现。从MSDN:“哈希函数用于快速生成对应于对象值的数字(哈希码)”。在哈希函数中分配新字符串似乎是个坏主意。
威尔卡

0

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();

编辑:忘记了空值。代码固定。


1
唯一的规则是UserInfo(str1 =“ AA”,str2 =“ BB”)。Equals(UserInfo(str1 =“ BB”,str2 =“ AA”))
alfred barthand 09年

-1

太复杂了,忘记了空值等等。它用于存储桶之类的东西,因此您可以摆脱诸如

if (null != str1) {
    return str1.GetHashCode();
}
if (null != str2) {
    return str2.GetHashCode();
}
//Not sure what you would put here, some constant value will do
return 0;

通过假设str1在很大比例的实例中不太可能是常见的,这是有偏见的。


这不满足str1和str2的顺序无关紧要的条件。(“ A”,“ B”)和(“ B”,“ A”)产生不同的哈希码。
塞巴斯蒂安·内格拉苏斯

6.5年后?您指的是什么条件?这是针对包含2个字符串的结构的哈希码生成的讨论,而不是比较2个字符串时发生的情况。
罗杰·威尔考克斯

结构(“ A”,“ B”)和(“ B”,“ A”)应视为相等。因此,它们的哈希码必须相等。但是(“ A”,“ B”)产生哈希码“ A”,而(“ B”,“ A”)产生哈希码“ B”-不相等。
塞巴斯蒂安·内格拉苏斯

考虑到该问题至少在最近6个月内已被编辑,因此我不确定该问题原本确实在此问题中。
罗杰·威尔考克斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.