试图理解GetHashCode()

时间:2013-08-16 13:01:27

标签: c# oop object hashcode

我在Microsoft文档中找到以下内容:

Two objects that are equal return hash codes that are equal. However, the reverse is not true: equal hash codes do not imply object equality, because different (unequal) objects can have identical hash code

我做了自己的测试来理解方法:

public static void HashMetod() 
{
    List<Cliente> listClientTest = new List<Cliente>
    {
        new Cliente { ID = 1, name = "Marcos", Phones = "2222"}
    };

    List<Empresa> CompanyList = new List<Empresa>
    {
        new Empresa { ID = 1, name = "NovaQuimica", Clients = listClientTest },
        new Empresa { ID = 1, name = "NovaQuimica", Clients = listClientTest }
    };

    CompanyList.Add(CompanyList[0]);

    foreach (var item in CompanyList)
    {
        Console.WriteLine("Hash code = {0}", item.GetHashCode());
    }

    Console.WriteLine("CompanyList[0].Equals(CompanyList[1]) = {0}", CompanyList[0].Equals(CompanyList[1]));
    Console.WriteLine("CompanyList[0].Equals(CompanyList[2]) = {0}", CompanyList[0].Equals(CompanyList[2]));
}

我的问题是:两个Differents对象如何返回相同的HashCode?我相信,如果两个对象返回相同,则它们是Equals(多数民众赞成我的方法显示)。执行我的方法并检查出来。

6 个答案:

答案 0 :(得分:9)

基于pigeonhole principle的简单观察:

  1. GetHashCode返回int - 32位整数。
  2. 有4294.967.296 32位整数;
  3. 仅考虑大写英文字母,有十个字母单词141.167.095.653.376。如果我们包括大写和小写,那么我们有144.555.105.949.057.024组合。
  4. 由于对象多于可用的哈希码,因此某些(不同的)对象必须具有相同的哈希码。
  5. 另一个更现实的例子是,如果你想给地球上的每个人一个哈希码,你就会发生冲突,因为我们有比32位整数更多的人。

    “有趣”的事实:由于birthday paradox,在一个拥有100,000人的城市中,你有超过50%的机会发生哈希冲突。

答案 1 :(得分:4)

这是一个例子;

String s1 = new String("AMY");
String s2 = new String("MAY");

两个不同的对象,但如果 hashCode是用字符的ASCII代码计算的,那么对于MAY和AMY,它将是相同< /强>

你基本上应该理解哈希的概念。

hashing an object means "finding a value (number) that can be reproduced by the very same instance again and again".

由于 Object.hashCode()的哈希码类型为int ,因此您只能拥有 2 ^ 32个不同的值。 这就是为什么当两个不同的对象产生相同的hashCode时,你会有所谓的“冲突”,具体取决于散列算法。

为了更好地理解它们,你可以通过一系列很好的例子来说明;

  1. PigeonHole, Sock Picking, Hair Counting
  2. SoftBall Team
  3. Birthday Problem
  4. 希望这有帮助。

答案 2 :(得分:1)

散列码是 int ,它有2 ^ 32个不同的值。现在让我们使用 String 类 - 它可以有无限多个不同的值,因此我们可以得出结论,对于不同的String值必须有相同的哈希码。

要查找哈希冲突,您可以利用生日悖论。例如,对于双打,它可能是

  random gen = new Random();

  Dictionary<int, Double> dict = new Dictionary<int, Double>();

  // In general it'll take about 
  // 2 * sqrt(2^32) = 2 * 65536 = 131072 = 1e5 itterations
  // to find out a hash collision (two unequal values with the same hash)  
  while (true) {
    Double d = gen.NextDouble();
    int key = d.GetHashCode();

    if (dict.ContainsKey(key)) {
      Console.Write(d.ToString(Culture.InvariantCulture));
      Console.Write(".GetHashCode() == ");

      Console.Write(dict[key].ToString(Culture.InvariantCulture));
      Console.Write(".GetHashCode() == ");
      Console.Write(key.ToString(Culture.InvariantCulture));

      break;
    }

    dict.Add(key, d);
   }

就我而言

  0.540086061479564.GetHashCode() == 0.0337553788133689.GetHashCode() == -1350313817

答案 3 :(得分:1)

您可以在维基页面上阅读hashing。但哈希的重点是将值转换为索引,这是通过散列函数完成的。散列函数可以有所不同,但几乎所有都以一个mod来结束索引值在一个最大值内,所以它可以放在一个数组中。对于每个mod n,有无限数量的数字将产生相同的索引(I.E. 5 mod 2,7 mod 2等)。

答案 4 :(得分:1)

您可能只需要阅读一般的Hash函数以确保您理解它。来自维基百科:

  

散列函数主要用于生成固定长度的输出数据   它充当对原始数据的缩短引用

所以基本上你知道你正在采取一大堆(可能是无限的)可能性,并试图将它们融入更小,更易于管理的可能性中。由于两个不同大小的集合,您可以保证在两个不同的源对象及其哈希值之间发生冲突。也就是说,一个好的Hash函数可以尽可能地减少这些碰撞。

答案 5 :(得分:0)

哈希代码的目的是允许接收对象的代码快速识别对象不可能等于的内容。如果一个被要求存储许多对象的集合类除了如何测试它们的相等性之外什么都不知道,那么就会给出另一个对象并被问到它是否匹配它存储的任何对象,集合必须调用Equals集合中的每个对象。另一方面,如果集合可以在添加到集合的每个项目上调用GetHashCode,以及它正在查找的项目,并且集合中99%的对象都报告了一个没有的哈希码t匹配正在搜索的项的哈希码,然后只需要检查哈希码匹配的1%的对象。

两个项目的哈希码匹配的事实无法比没有检查其哈希码的情况更快地比较这两个项目,但项目的哈希码不匹配的事实将消除任何需要进一步检查它们。在场景中,项目更不可能匹配而不是匹配,哈希码可以加速不匹配的情况,有时可以达到很多个数量级。