为什么这些哈希码等于?

时间:2013-01-29 14:19:14

标签: .net-4.0 gethashcode

此测试失败:

var hashCode = new 
{
    CustomerId = 3354,
    ServiceId = 3,
    CmsThematicId = (int?)605,
    StartDate = (DateTime?)new DateTime(2013, 1, 5),
    EndDate = (DateTime?)new DateTime(2013, 1, 6)
}.GetHashCode();
var hashCode2 = new
{
    CustomerId = 1210,
    ServiceId = 3,
    CmsThematicId = (int?)591,
    StartDate = (DateTime?)new DateTime(2013, 3, 31),
    EndDate = (DateTime?)new DateTime(2013, 4, 1)
}.GetHashCode();
Assert.AreNotEqual(hashCode, hashCode2);

你能告诉我为什么吗?

4 个答案:

答案 0 :(得分:3)

你发现这个巧合有点神奇。

匿名类具有生成的GetHashCode()方法,该方法通过组合所有属性的哈希码来生成哈希码。

计算基本上是这样的:

  public override int GetHashCode()
  {
    return        -1521134295 * 
                ( -1521134295 * 
                ( -1521134295 * 
                ( -1521134295 * 
                ( -1521134295 * 
                   1170354300 + 
                  CustomerId.GetHashCode()) +
                  ServiceId.GetHashCode()) + 
                  CmsThematicId.GetHashCode()) + 
                  StartDate.GetHashCode()) + 
                  EndDate.GetHashCode();
  }

如果更改任何字段的任何值,则哈希码会发生变化。事实上,您发现两组不同的值碰巧得到相同的哈希码,这是巧合。

请注意,哈希码不一定是唯一的。不可能说哈希码总是唯一的,因为可能存在比哈希码更多的对象(尽管这是很多对象)。好的哈希码提供值的随机分布。

注意:以上内容来自.NET 4.不同版本的.NET可能不同,Mono也不同。

如果要实际比较两个对象是否相等,请使用.Equals()。对于匿名对象,它会比较每个字段。更好的选择是使用NUnit约束来比较每个字段并报告哪个字段不同。我在这里发布了一个约束:

https://stackoverflow.com/a/2046566/118703

答案 1 :(得分:1)

处理大量数据时是否遇到过这种情况?

欢迎来到哈希码的精彩世界。哈希码不是“唯一标识符”。它不可能。该匿名类型的可能的不同实例基本上是无限数量,但只有2 ^ 32个可能的哈希码。因此,如果您创建了足够多的这些对象,那么您将会看到一些重复的内容。实际上,如果你随机生成70,000个这些对象,那么其中两个具有相同哈希码的几率要好于50%。

有关详细信息,请参阅Birthdays, Random Numbers, and Hash Codes和链接的维基百科文章。

至于为什么有些人没有看到重复而其他人没有看到重复,他们可能会在不同版本的.NET上运行该程序。用于生成哈希码的算法不保证在不同版本或平台上保持相同:

  

对象的GetHashCode方法必须始终返回相同的值   哈希码只要没有对象状态的修改即可   确定对象的Equals方法的返回值。 请注意   这仅适用于当前执行的应用程序,并且   如果运行应用程序,则可以返回不同的哈希代码   再次

答案 2 :(得分:1)

您的测试无效。

由于哈希码不能保证唯一(请参阅其他答案以获得更好的解释),因此不应测试哈希码的唯一性。

在编写自己的GetHashCode()方法时,最好测试随机输入的均匀分布,而不是唯一性。只需确保使用足够的随机输入即可获得良好的测试结果。

GetHashCode上的MSDN规范具体说明:

  

为获得最佳性能,哈希函数必须生成随机数   所有输入的分配。

当然,这都是相对的。用于将100个对象放入字典中的GetHashCode()方法不需要像将{10,000}对象放入字典中的GetHashCode()那样随机。

答案 3 :(得分:0)

Jim建议我(在聊天室中)存储我的参数,这样当我显示我的参数时,选择未使用的参数,然后当有人注册时我将其标记为已使用。但是生成所有参数是一个很大的PITA。

所以我的解决方案就是像这样构建一个int64哈希码

const long i = -1521134295;    
return -i * (-i * (-i * (-i * -117147284 + customerId.GetHashCode()) + serviceId.GetHashCode()) + cmsThematicId.GetHashCode()) + startDate.GetHashCode();

我删除了结束日期,因为它的值取决于serviceId和startDate,所以我不应该将它添加到第一个地方的哈希码中。 我从生成的类的反编译中复制/粘贴它。如果我用300 000种不同的组合进行测试,我就没有进行彻底检查。