如果我从未使用过HashSet,我还应该实现GetHashCode吗?

时间:2010-05-27 12:32:46

标签: c#

我永远不需要在哈希表中存储对象。原因有两个:

  • 提出良好的哈希函数很困难且容易出错。
  • AVL树几乎总是足够快,它只需要一个严格的顺序谓词,这更容易实现。

另一方面,Equals()操作是一种非常常用的功能。

因此我想知道在实现Equals函数(我经常需要)时是否有必要实现GetHashCode(我从不需要)?

7 个答案:

答案 0 :(得分:13)

我的建议 - 如果您不想使用它,请覆盖它并throw new NotImplementedException();,以便您可以看到您需要它。

答案 1 :(得分:5)

如果您认为实现严格的顺序谓词比散列函数更容易实现,我认为您错了 - 它需要处理大量边缘情况(空值,类层次结构)。和散列函数aren't that difficult,真的。

答案 2 :(得分:4)

AVL树比哈希表慢得多。如果您只处理几件物品,那么这不会是一个大问题。 Hashtables有O(1)插入,删除和搜索,但AVL树有O(log(n))操作。

我会继续覆盖GetHashCodeEquals,原因有两个。

  • 通过使用简单的XOR实现来获得合理的分布并不困难。 1
  • 如果您的类是公共API的一部分,那么其他人可能希望将它们存储在哈希表中。

另外,我不得不质疑BST的选择。 AVL树有点不合时宜。还有其他更现代的BST更容易实现和工作(有时更好)。如果您确实需要一个维护排序的数据结构,那么请考虑这些替代方案。


1 XOR策略存在一个微妙的关联性问题,在a^b = b^a之后的某些情况下可能会导致冲突。来自Effective Java的解决方案已经实现了类似邪教的识别,这也很容易实现。

答案 3 :(得分:3)

如果您使用DictionarySortedList并覆盖Equals,则需要使用哈希函数,否则它们会中断。 {B}中也会使用Equals,如果其他人使用您的对象,他们会期望GetHashCode能够合理地行事。

请注意,哈希函数不必那么复杂。基本版本是获取您用于相等的任何成员变量的哈希值,将每个成员变量与一个单独的互质数相乘,并将它们一起进行异或。

答案 4 :(得分:2)

您无需实施它。如果你编写自己的Equals()方法,我建议使用一些不破坏HashSet的GetHashCode实现。例如,您可以返回静态值(通常为42)。 HashSet性能会急剧下降,但至少它仍然可以工作 - 你永远不会知道将来谁会使用/编辑/维护你的代码。 (编辑:如果在散列结构中使用这样的类以便及早发现性能问题,则可能需要记录警告)

编辑:不要只使用XOR来合并属性的哈希码

其他人已经说过,您可以简单地组合所有属性的哈希码。而不是只使用XOR我会鼓励倍增结果。如果两个值相等(例如0xA ^ 0xA == 0x0),则XOR可能导致0值。使用0xA * 0xA0xA * 31 + 0xA0xA ^ (0xA * 31)可以轻松改善这种情况。

尽管如此,我的答案的意图是任何哈希函数都优于与equals不一致的哈希函数 - 即使它只返回一个静态值。只需选择用于相等性的任何属性子集(从none到all)并将结果放在一起。在选择哈希码的属性时,更喜欢那些组合非常独特的小子集(例如,名字,姓氏,生日 - 不需要添加整个地址)

答案 5 :(得分:1)

使用足够的哈希函数很难。大多数情况下,对所有字段GetHashCode()的结果进行简单的XOR就足够了。

答案 6 :(得分:1)

如果覆盖equals,则应覆盖MSDN中的GetHashCode():“建议任何覆盖Equals的类也覆盖System.Object.GetHashCode。” http://msdn.microsoft.com/en-us/library/ms173147.aspx

这两个函数应该匹配,如果两个对象相等,它们应该具有相同的散列值。这并不意味着如果两个对象具有相同的散列,则它们应该相等。您不需要过于复杂的哈希算法,但它应该尝试在整数空间内很好地分布。