Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
327 views
in Technique[技术] by (71.8m points)

.net - 重写GetHashCode的最佳算法是什么?(What is the best algorithm for overriding GetHashCode?)

In .NET, the GetHashCode method is used in a lot of places throughout the .NET base class libraries.

(在.NET中,整个.NET基类库的许多地方都使用GetHashCode方法 。)

Implementing it properly is especially important to find items quickly in a collection or when determining equality.

(正确实施它对于在集合中或确定相等性时快速查找项目尤为重要。)

Is there a standard algorithm or best practice on how to implement GetHashCode for my custom classes so I don't degrade performance?

(有没有关于如何为我的自定义类实现GetHashCode的标准算法或最佳实践,因此我不会降低性能?)

  ask by bitbonk translate from so

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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:)

  • You can compute the hash code from fields that are not mutable;

    (您可以从不可变的字段中计算哈希码;)

    or

    (要么)

  • You can ensure that the hash code of a mutable object does not change while the object is contained in a collection that relies on its hash code.

    (您可以确保在对象包含在依赖于其哈希代码的集合中时,该可变对象的哈希代码不会更改。)


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...