为包含集合的对象实现 GetHashCode()
本文关键字:GetHashCode 实现 对象 包含 集合 | 更新日期: 2023-09-27 18:33:55
考虑以下对象:
class Route
{
public int Origin { get; set; }
public int Destination { get; set; }
}
路由实现相等运算符。
class Routing
{
public List<Route> Paths { get; set; }
}
我使用下面的代码为路由对象实现 GetHashCode 方法,它似乎有效,但我想知道这是否是正确的方法?我依靠平等检查,因为我不确定,我想我会问你们。我可以只对哈希码求和,还是需要做更多的魔术来保证预期的效果?
public override int GetHashCode() =>
{
return (Paths != null
? (Paths.Select(p => p.GetHashCode())
.Sum())
: 0);
}
我在这里检查了几个GetHashCode()
问题以及MSDN和Eric Lippert关于此主题的文章,但找不到我要找的内容。
我认为您的解决方案很好。(很久以后的评论:LINQ 的 Sum
方法将在checked
上下文中起作用,因此您可以轻松地获得OverflowException
这意味着它毕竟不是那么好。但更常见的是做异或(不带进位的加法(。所以它可能是这样的
public override int GetHashCode()
{
int hc = 0;
if (Paths != null)
foreach (var p in Paths)
hc ^= p.GetHashCode();
return hc;
}
附录(答复被接受后(:
请记住,如果您在Dictionary<Routing, Whatever>
、HashSet<Routing>
或其他使用哈希表的情况下使用这种类型的Routing
,那么如果有人在将实例添加到集合后更改(更改(Routing
,您的实例将丢失。
如果您确定这永远不会发生,请使用我上面的代码。 如果您确保没有人更改引用的Routing
,Dictionary<,>
等等仍然有效。
另一种选择是只写
public override int GetHashCode()
{
return 0;
}
如果您认为哈希代码永远不会被使用。如果每个 instace 都返回哈希代码的0
,那么哈希表的性能会非常糟糕,但您的对象不会丢失。第三种选择是抛出NotSupportedException
。
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;
}
随着时间的推移,随着越来越多的项目被散列,此代码至少会涉及所有位。
作为准则,对象的哈希值在对象的整个生命周期内必须相同。我会不理会GetHashCode
函数,并且不会覆盖它。仅当您要将对象放入哈希表中时才使用哈希代码。
你应该阅读 Eric Lippert 关于 .NET 中哈希代码的精彩文章:GetHashCode 的指南和规则。
引自那篇文章:
准则:GetHashCode 返回的整数永远不应更改
规则:当对象包含在依赖于哈希代码保持稳定的数据结构中时,GetHashCode 返回的整数不得更改
如果对象的哈希代码在哈希表中时可能会发生变化,则显然 Include 方法停止工作。你把对象放在桶 #5 中,你改变它,当你问集合它是否包含变异的对象时,它会在桶 #74 中查找并且没有找到它。
您实现的 GetHashCode
函数在对象的生存期内不会返回相同的哈希代码。如果使用此函数,则在将这些对象添加到哈希表中时会遇到麻烦:Contains
方法将不起作用。
一种正确的方法,因为最终hashcode
它对于指定的对象必须是唯一的。在您的情况下,您可以执行一个Sum()
,它可以在集合中使用不同的哈希码产生相同的结果(最后哈希码只是整数(。
如果您打算根据集合的内容确定相等性,此时只需比较两个对象之间的这些参数即可。顺便说一下,这可能是耗时的操作。