为包含集合的对象实现GetHashCode()

时间:2012-05-12 21:13:41

标签: c# gethashcode

考虑以下对象:

class Route
{
   public int Origin { get; set; }
   public int Destination { get; set; }
}

Route实现了相等的运算符。

class Routing
{
   public List<Route> Paths { get; set; }
}

我使用下面的代码为Routing对象实现GetHashCode方法,它似乎有效,但我想知道这是否是正确的方法呢?我依靠平等检查,因为我不确定我以为我会问你们。我可以只对哈希码求和,还是需要做更多的魔法以保证所需的效果?

public override int GetHashCode() =>
{
    return (Paths != null 
                ? (Paths.Select(p => p.GetHashCode())
                        .Sum()) 
                : 0);
}

我在这里检查了几个GetHashCode()个问题,以及MSDN和Eric Lippert关于这个主题的文章,但找不到我想要的内容。

4 个答案:

答案 0 :(得分:14)

我认为您的解决方案很好。 (后来的评论很多:LINQ的Sum方法将在checked上下文中运行,所以你很容易得到一个OverflowException,这意味着它毕竟不是那么好。)但它更多通常做XOR(无需携带)。所以它可能像

public override int GetHashCode()
{
  int hc = 0;
  if (Paths != null)
    foreach (var p in Paths)
      hc ^= p.GetHashCode();
  return hc;
}

附录(接受回答后):

请记住,如果您在RoutingDictionary<Routing, Whatever>或其他使用哈希表的情况下使用此类型HashSet<Routing>,那么您的实例将丢失< / strong>如果有人在Routing添加到集合后改变(变异)。

如果您确定永远不会发生,请使用上面的代码。如果您确保没有人更改所引用的Dictionary<,>,那么Routing等仍然可以使用。

另一种选择是写

public override int GetHashCode()
{
  return 0;
}

如果您认为永远不会使用哈希码。如果每个instace都返回0哈希码,那么哈希表的性能会非常差,但是你的对象不会丢失。第三种选择是抛出NotSupportedException

答案 1 :(得分:7)

来自Jeppe Stig Nielsen的答案的代码可行,但它可能导致大量重复哈希码值。假设您正在哈希一个0-100范围内的整数列表,那么您的哈希码将被保证在0到255之间。这在字典中使用时会产生大量冲突。这是一个改进版本:

public override int GetHashCode()
{
  int hc = 0;
  if (Paths != null)
    foreach (var p in Paths) {
        hc ^= p.GetHashCode();
        hc = (hc << 7) | (hc >> (32 - 7)); //rotale hc to the left to swipe over all bits
    }
  return hc;
}

随着越来越多的项目被散列,此代码至少会涉及所有位。

答案 2 :(得分:5)

作为指导原则,对象的哈希值必须与对象的整个生命周期相同。我会单独留下GetHashCode函数,而不是覆盖它。只有在想要将对象放在哈希表中时才使用哈希码。

您应该阅读Eric Lippert关于.NET中哈希码的精彩文章:Guidelines and rules for GetHashCode

引用该文章:

  

指南:GetHashCode返回的整数永远不会改变

     

规则:当对象包含在依赖于哈希代码保持稳定的数据结构中时,GetHashCode返回的整数必须永远不会改变

     

如果对象的哈希码在哈希表中变异,那么显然Contains方法就会停止工作。你把对象放在#5桶中,你改变了它,当你询问它是否包含变异对象时,它会在#74桶中查找并找不到它。

您实现的GetHashCode函数在对象的生命周期内不会返回相同的哈希码。如果您使用此功能,如果将这些对象添加到哈希表中,则会遇到麻烦: Contains方法无效

答案 3 :(得分:0)

我认为这不是一种正确的方法,因此要确定最终hashcode它必须对指定的对象是唯一的。在您的情况下,您执行Sum(),它可以使用集合中的不同哈希码生成相同的结果(在末尾哈希码只是整数)。

如果您打算根据集合的内容确定相等性,那么只需比较两个对象之间的这些截距。顺便说一下,它可能是耗时的操作。