I usually go with something like the implementation given in Josh Bloch's fabulous Effective Java .
(我通常会使用类似于Josh Bloch 出色的 Effective Java中给出的实现的东西。)
It's fast and creates a pretty good hash which is unlikely to cause collisions. (它速度很快,并且创建了一个很好的哈希,不太可能导致冲突。)
Pick two different prime numbers, eg 17 and 23, and do: (选择两个不同的质数,例如17和23,然后执行:)
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = 17;
// Suitable nullity checks etc, of course :)
hash = hash * 23 + field1.GetHashCode();
hash = hash * 23 + field2.GetHashCode();
hash = hash * 23 + field3.GetHashCode();
return hash;
}
}
As noted in comments, you may find it's better to pick a large prime to multiply by instead.
(如评论中所述,您可能会发现最好选择一个大质数乘以。)
Apparently 486187739 is good... and although most examples I've seen with small numbers tend to use primes, there are at least similar algorithms where non-prime numbers are often used. (显然486187739是个好方法……虽然我看到的大多数带有小数的示例都倾向于使用质数,但至少有类似的算法经常使用非质数。)
In the not-quite- FNV example later, for example, I've used numbers which apparently work well - but the initial value isn't a prime. (例如,在稍后的非FNV示例中,我使用的数字似乎很有效-但初始值不是素数。)
(The multiplication constant is prime though. I don't know quite how important that is.) ((尽管乘法常数是素数。我不知道它的重要性。))
This is better than the common practice of XOR
ing hashcodes for two main reasons.
(由于两个主要原因,这比对哈希码进行XOR
的常规做法更好。)
Suppose we have a type with two int
fields: (假设我们有一个带有两个int
字段的类型:)
XorHash(x, x) == XorHash(y, y) == 0 for all x, y
XorHash(x, y) == XorHash(y, x) for all x, y
By the way, the earlier algorithm is the one currently used by the C# compiler for anonymous types.
(顺便说一下,较早的算法是C#编译器当前用于匿名类型的算法。)
This page gives quite a few options.
(此页面提供了很多选项。)
I think for most cases the above is "good enough" and it's incredibly easy to remember and get right. (我认为,在大多数情况下,以上内容“足够好”,并且容易记住和正确使用。)
The FNV alternative is similarly simple, but uses different constants and XOR
instead of ADD
as a combining operation. (FNV替代方案同样简单,但使用不同的常量和XOR
代替ADD
作为组合操作。)
It looks something like the code below, but the normal FNV algorithm operates on individual bytes, so this would require modifying to perform one iteration per byte, instead of per 32-bit hash value. (它看起来像下面的代码,但正常的FNV算法对每个字节进行操作,所以这将需要修改来执行的,而不是每32位的哈希值每字节一个迭代。)
FNV is also designed for variable lengths of data, whereas the way we're using it here is always for the same number of field values. (FNV还设计用于可变长度的数据,而我们在这里使用它的方式始终是针对相同数量的字段值。)
Comments on this answer suggest that the code here doesn't actually work as well (in the sample case tested) as the addition approach above. (对这个答案的评论表明,这里的代码实际上不如上面的添加方法那样有效(在测试的示例案例中)。)
// Note: Not quite FNV!
public override int GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
int hash = (int) 2166136261;
// Suitable nullity checks etc, of course :)
hash = (hash * 16777619) ^ field1.GetHashCode();
hash = (hash * 16777619) ^ field2.GetHashCode();
hash = (hash * 16777619) ^ field3.GetHashCode();
return hash;
}
}
Note that one thing to be aware of is that ideally you should prevent your equality-sensitive (and thus hashcode-sensitive) state from changing after adding it to a collection that depends on the hash code.
(请注意,需要注意的一件事是,理想情况下,应在将其对等值敏感(因此对哈希码敏感)的状态添加到依赖于哈希码的集合中之后,再进行更改。)
As per the documentation :
(根据文档 :)
You can override GetHashCode for immutable reference types.
(您可以重写GetHashCode以获取不可变的引用类型。)
In general, for mutable reference types, you should override GetHashCode only if: (通常,对于可变引用类型,仅在以下情况下才应覆盖GetHashCode:)