【问题标题】:Why HashSets with same elements return different values when calling to GetHashCode()?为什么调用GetHashCode()时具有相同元素的HashSets返回不同的值?
【发布时间】:2018-11-25 21:44:34
【问题描述】:

为什么HashSet<T>.GetHashCode() 具有相同元素时返回不同的哈希码?

例如:

[Fact]
public void EqualSetsHaveSameHashCodes()
{
    var set1 = new HashSet<int>(new [] { 1, 2, 3 } );
    var set2 = new HashSet<int>(new [] { 1, 2, 3 } );

    Assert.Equal(set1.GetHashCode(), set2.GetHashCode());
}

此测试失败。为什么?

我怎样才能得到我需要的结果? “相等的集合给出相同的哈希码”

【问题讨论】:

  • 您必须实现自己的基于元素的 GetHashCode 函数。
  • 查看文档以了解 Hashset.GetHashCode 方法 docs.microsoft.com/en-us/dotnet/api/…
  • 你的哈希是比较两个数组,而不是数组的内容。
  • 等集 do 给出相同的哈希码。您的 set1set2 不相等,因为它们是不同的对象。测试它; set1.Equals(set2) 返回false
  • 这感觉像是一个 XY 问题 - meta.stackexchange.com/questions/66377/what-is-the-xy-problem。你为什么关心HashCode?您的真正的潜在问题是什么?

标签: c# .net set hashcode


【解决方案1】:

HashSet&lt;T&gt; 默认情况下没有值相等语义。它具有引用相等语义,因此即使包含的元素相同,两个不同的哈希集也不会相等或具有相同的哈希码。

您需要使用特殊用途的IEqualityComparer&lt;HashSet&lt;int&gt;&gt; 来获得您想要的行为。您可以自己滚动或使用框架为您提供的默认值:

var hashSetOfIntComparer = HashSet<int>.CreateSetComparer();

//will evaluate to true
var haveSameHash = hashSetOfIntComparer.GetHashCode(set1) ==
                   hashSetOfIntComparer.GetHashCode(set2);

所以,长话短说:

我怎样才能得到我需要的结果? “相等的集合给出相同的哈希码”

如果您计划使用HashSet&lt;T&gt;.GetHashCode() 的默认实现,则不能。您要么使用特殊用途的比较器,要么扩展 HashSet&lt;T&gt; 并覆盖 EqualsGetHashCode 以满足您的需求。

【讨论】:

    【解决方案2】:

    默认情况下(除非另有特别说明),引用类型仅在引用相同对象时才被视为相等。作为开发人员,您可以重写 Equals() 和 GetHashCode() 方法,以便您认为相等的对象对 Equals 返回 true,对 GetHashCode 返回相同的 int。

    根据您使用的测试框架,将有一个 CollectionAssert.AreEquivalent() 或一个对 Assert.Equal 的覆盖,它需要一个比较器。

    【讨论】:

      【解决方案3】:

      您可以实现一个自定义 HashSet,它会覆盖 GetHashCode 函数,该函数会从所有内容中生成一个新的哈希码,如下所示:

      public class HashSetWithGetHashCode<T> : HashSet<T>
      {
          public override int GetHashCode()
          {
              unchecked // Overflow is fine, just wrap
              {
                  int hash = 17;
                  foreach (var item in this)
                      hash = hash * 23 + item.GetHashCode();
                  return hash;
              }
          }
      }
      

      【讨论】:

      • 这会给你一个 O(n) 的复杂度,这是不希望得到哈希码的。恐怕这就是投反对票的原因。
      • 我没有看到任何比 O(N) 更快的方法,但更令人担忧的是,这种实现取决于排序,这不适合 HashSet。
      • 很好地抓住了乔恩·斯基特。除了前面提到的性能问题,是否可以在这个函数中对元素进行排序,或者保留一个并行的已排序项集合,在其中对哈希进行操作?
      猜你喜欢
      • 2016-04-10
      • 2022-11-29
      • 2012-07-20
      • 2017-03-29
      • 1970-01-01
      • 1970-01-01
      • 2012-08-13
      • 2015-10-20
      • 1970-01-01
      相关资源
      最近更新 更多