重写System.Object.GetHashCode的最佳算法是什么?

重写System.Object.GetHashCode的最佳算法是什么?

在.NET System.Object.GetHashCode方法中,很多地方都使用.NET 方法。特别是在快速查找集合中的项目或确定相等性时。是否有关于如何GetHashCode为我的自定义类实现覆盖的标准算法/最佳实践,因此我不会降低性能?



米琪卡哇伊
浏览 845回答 4
4回答

白板的微信

我通常会使用Josh Bloch 精彩的 Effective Java中的实现。它很快并且创建了一个非常好的哈希,不太可能导致冲突。选择两个不同的素数,例如17和23,并执行:public override int GetHashCode(){     unchecked // Overflow is fine, just wrap     {         int hash = 17;         // Suitable nullity checks etc, of course :)         hash = hash * 23 + field1.GetHashCode();         hash = hash * 23 + field2.GetHashCode();         hash = hash * 23 + field3.GetHashCode();         return hash;     }}正如评论中所指出的,你可能会发现最好选择一个大素数乘以。显然486187739是好的...虽然我看到的大多数例子都是小数字倾向于使用素数,但至少有类似的算法,其中经常使用非素数。例如,在后来的非FNV例子中,我使用的数字显然效果很好 - 但初始值不是素数。(乘法常数虽然是素数。我不知道它有多重要。)这比XOR出于哈希码的常见做法更好,主要有两个原因。假设我们有一个包含两个int字段的类型:XorHash(x, x) == XorHash(y, y) == 0 for all x, yXorHash(x, y) == XorHash(y, x) for all x, y顺便说一下,早期的算法是C#编译器当前用于匿名类型的算法。这个页面提供了很多选择。我认为在大多数情况下,上述情况“足够好”并且非常容易记住并且正确。所述FNV替代方案是同样简单,但使用不同的常数和XOR代替ADD作为组合操作。它看起来的东西像下面的代码,但正常的FNV算法对每个字节进行操作,所以这将需要修改来执行的,而不是每32位的哈希值每字节一个迭代。FNV也是为可变长度的数据而设计的,而我们在这里使用它的方式总是针对相同数量的字段值。对这个答案的评论表明,这里的代码实际上并不像上面的添加方法那样(在测试的示例中)。// Note: Not quite FNV!public override int GetHashCode(){     unchecked // Overflow is fine, just wrap     {         int hash = (int) 2166136261;         // Suitable nullity checks etc, of course :)         hash = (hash * 16777619) ^ field1.GetHashCode();         hash = (hash * 16777619) ^ field2.GetHashCode();         hash = (hash * 16777619) ^ field3.GetHashCode();         return hash;     }}请注意,有一点需要注意的是,理想情况下,在将其添加到依赖于哈希代码的集合之后,应该防止对等式敏感(因此对哈希码敏感)状态的更改。根据文件:您可以为不可变引用类型重写GetHashCode。通常,对于可变引用类型,只有在以下情况下才应覆盖GetHashCode:您可以从不可变的字段计算哈希码; 要么您可以确保在对象包含在依赖于其哈希代码的集合中时,可变对象的哈希码不会更改。

饮歌长啸

这是我的哈希码助手。它的优点是它使用泛型类型参数,因此不会导致装箱:public&nbsp;static&nbsp;class&nbsp;HashHelper{ &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;int&nbsp;GetHashCode<T1,&nbsp;T2>(T1&nbsp;arg1,&nbsp;T2&nbsp;arg2) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unchecked &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;31&nbsp;*&nbsp;arg1.GetHashCode()&nbsp;+&nbsp;arg2.GetHashCode(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;int&nbsp;GetHashCode<T1,&nbsp;T2,&nbsp;T3>(T1&nbsp;arg1,&nbsp;T2&nbsp;arg2,&nbsp;T3&nbsp;arg3) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unchecked &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;hash&nbsp;=&nbsp;arg1.GetHashCode(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hash&nbsp;=&nbsp;31&nbsp;*&nbsp;hash&nbsp;+&nbsp;arg2.GetHashCode(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;31&nbsp;*&nbsp;hash&nbsp;+&nbsp;arg3.GetHashCode(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;int&nbsp;GetHashCode<T1,&nbsp;T2,&nbsp;T3,&nbsp;T4>(T1&nbsp;arg1,&nbsp;T2&nbsp;arg2,&nbsp;T3&nbsp;arg3,&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;T4&nbsp;arg4) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unchecked &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;hash&nbsp;=&nbsp;arg1.GetHashCode(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hash&nbsp;=&nbsp;31&nbsp;*&nbsp;hash&nbsp;+&nbsp;arg2.GetHashCode(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hash&nbsp;=&nbsp;31&nbsp;*&nbsp;hash&nbsp;+&nbsp;arg3.GetHashCode(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;31&nbsp;*&nbsp;hash&nbsp;+&nbsp;arg4.GetHashCode(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;int&nbsp;GetHashCode<T>(T[]&nbsp;list) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unchecked &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;hash&nbsp;=&nbsp;0; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach&nbsp;(var&nbsp;item&nbsp;in&nbsp;list) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hash&nbsp;=&nbsp;31&nbsp;*&nbsp;hash&nbsp;+&nbsp;item.GetHashCode(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;hash; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;int&nbsp;GetHashCode<T>(IEnumerable<T>&nbsp;list) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unchecked &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;hash&nbsp;=&nbsp;0; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach&nbsp;(var&nbsp;item&nbsp;in&nbsp;list) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hash&nbsp;=&nbsp;31&nbsp;*&nbsp;hash&nbsp;+&nbsp;item.GetHashCode(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;hash; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;<summary> &nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;Gets&nbsp;a&nbsp;hashcode&nbsp;for&nbsp;a&nbsp;collection&nbsp;for&nbsp;that&nbsp;the&nbsp;order&nbsp;of&nbsp;items&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;does&nbsp;not&nbsp;matter. &nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;So&nbsp;{1,&nbsp;2,&nbsp;3}&nbsp;and&nbsp;{3,&nbsp;2,&nbsp;1}&nbsp;will&nbsp;get&nbsp;same&nbsp;hash&nbsp;code. &nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;</summary> &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;int&nbsp;GetHashCodeForOrderNoMatterCollection<T>( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;IEnumerable<T>&nbsp;list) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unchecked &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;hash&nbsp;=&nbsp;0; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;count&nbsp;=&nbsp;0; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;foreach&nbsp;(var&nbsp;item&nbsp;in&nbsp;list) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hash&nbsp;+=&nbsp;item.GetHashCode(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;count++; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;31&nbsp;*&nbsp;hash&nbsp;+&nbsp;count.GetHashCode(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;<summary> &nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;Alternative&nbsp;way&nbsp;to&nbsp;get&nbsp;a&nbsp;hashcode&nbsp;is&nbsp;to&nbsp;use&nbsp;a&nbsp;fluent&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;interface&nbsp;like&nbsp;this:<br&nbsp;/> &nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;return&nbsp;0.CombineHashCode(field1).CombineHashCode(field2). &nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CombineHashCode(field3); &nbsp;&nbsp;&nbsp;&nbsp;///&nbsp;</summary> &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;static&nbsp;int&nbsp;CombineHashCode<T>(this&nbsp;int&nbsp;hashCode,&nbsp;T&nbsp;arg) &nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;unchecked &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;31&nbsp;*&nbsp;hashCode&nbsp;+&nbsp;arg.GetHashCode();&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;}它还有扩展方法来提供流畅的界面,所以你可以像这样使用它:public&nbsp;override&nbsp;int&nbsp;GetHashCode(){ &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;HashHelper.GetHashCode(Manufacturer,&nbsp;PartN,&nbsp;Quantity);}或者像这样:public&nbsp;override&nbsp;int&nbsp;GetHashCode(){ &nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;0.CombineHashCode(Manufacturer) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.CombineHashCode(PartN) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;.CombineHashCode(Quantity);}
打开App,查看更多内容
随时随地看视频慕课网APP