C# 如果我从来没有使用过HashSet,我还应该实现GetHashCode吗?

C# 如果我从来没有使用过HashSet,我还应该实现GetHashCode吗?,c#,C#,我从不需要在哈希表中存储对象。原因有两方面: 想出一个好的散列函数既困难又容易出错 AVL树几乎总是足够快,它只需要一个严格的顺序谓词,这更容易实现 另一方面,Equals()操作是一个非常常用的函数 因此,我想知道在实现Equals函数(我经常需要)时是否有必要实现GetHashCode(我从不需要)?您不需要实现它。如果您编写自己的Equals()方法,我建议您使用一些不会破坏HashSet的GetHashCode实现。例如,您可以返回一个静态值(通常为42)。哈希集的性能会急剧下降,但

我从不需要在哈希表中存储对象。原因有两方面:

  • 想出一个好的散列函数既困难又容易出错
  • AVL树几乎总是足够快,它只需要一个严格的顺序谓词,这更容易实现
另一方面,Equals()操作是一个非常常用的函数


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

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

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

其他人已经说过,您可以简单地组合所有属性的哈希代码。但我不想只使用XOR,而是鼓励将结果相乘。如果两个值相等(例如,
0xA^0xA==0x0
),则XOR可能导致0值。使用
0xA*0xA
0xA*31+0xA
0xA^(0xA*31)
可以很容易地改善这一点


尽管如此,我的回答的目的是,任何哈希函数都比不与equals一致的哈希函数好——即使它只返回一个静态值。只需选择用于相等的任何属性子集(从“无”到“全部”),然后将结果放在一起。在为哈希代码选择属性时,请选择那些组合非常独特的小子集(例如,firstname、lastname、birthday-无需添加整个地址)

我的建议-如果您不想使用它,请覆盖它并
抛出new NotImplementedException()以便查看您在哪里需要它。

如果您使用
字典或
分类列表
,并覆盖
等于
,您需要有一个哈希函数,否则它们将中断
Equals
在BCL中的所有位置都被使用,如果其他人使用您的对象,他们会期望
GetHashCode
表现得明智


请注意,哈希函数不必那么复杂。一个基本的版本是对所有用于相等的成员变量进行散列,将每个变量与一个单独的互质数相乘,然后将它们异或在一起。

找到一个合适的散列函数并不困难。大多数情况下,对所有字段的
GetHashCode()
结果进行简单的异或运算就足够了。

如果您认为实现严格顺序谓词比实现哈希函数容易得多,那么我认为您是大错特错的-它需要处理大量的边缘情况(空值、类层次结构)。和散列函数。

如果重写equals,则应重写MSDN中的GetHashCode:“建议重写equals的任何类也重写System.Object.GetHashCode。”


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

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

我会继续重写
GetHashCode
Equals
,原因有两个

  • 使用一个简单的XOR实现来获得一个合适的发行版其实并不难。1
  • 如果您的类是公共API的一部分,那么其他人可能希望将它们存储在哈希表中
另外,我不得不质疑BST的选择。AVL树现在有点过时了。还有其他更现代的BST更容易实现,并且工作也一样好(有时更好)。如果你真的需要一个维护排序的数据结构,那么考虑这些备选方案。
  • -已由执行


1XOR策略有一个微妙的关联性问题,在某些情况下可能会导致冲突,因为
a^b=b^a
。有一个解决方案可以实现类似邪教的认可,而且实现起来也相当简单。

这是一个非常好的主意。我想知道为什么默认实现不能做到这一点@Dimitri:因为默认实现是用于引用标识的,这在很多情况下已经足够了。您可以使用
object
作为键,默认情况下,您构造的每个
object
都是唯一的:
var key=new object()HashKey
,它就是带有额外方法的
object
。此外,每个对象本身都可以用作键,即使具有相同内容的两个对象不被认为是相等的,这样您就可以在查找表中将它们用作键来查找相关对象。@Rubys毫不奇怪,真的:)甚至只是对组成变量的哈希代码进行异或运算都非常简单,并且提供了一个相当好的分布。正如您所说,您不必使用困难的实现。@Brian only XOR不是最佳选择,因为在某些情况下,您可能最终得到0。请参阅我的编辑如果两个不同的变量可以交换其属性,它也会崩溃。例如,具有值(“hello”、“w”)的对象