![](/img/trans.png)
[英]What is the best way to implement GetHashCode() for class with lots of properties?
[英]What is the best way to implement this composite GetHashCode()
我有一個簡單的類:
public class TileName {
int Zoom, X, Y;
public override bool Equals (object obj)
{
var o = obj as TileName;
return (o != null) && (o.Zoom == Zoom) && (o.X == X) && (o.Y == Y);
}
public override int GetHashCode ()
{
return (Zoom + X + Y).GetHashCode();
}
}
我很好奇,如果我改為執行以下操作,是否會獲得更好的哈希碼分布:
public override int GetHashCode ()
{
return Zoom.GetHashCode() + X.GetHashCode() + Y.GetHashCode();
}
這個類將用作字典鍵,所以我想確保有一個合適的分布。
就像 Jon Skeet 在這個 SO answer 中所描述的那樣,最佳實踐是選擇一些素數並將它們與單個哈希碼相乘,然后將所有內容相加。
public int GetHashCode()
{
unchecked
{
int hash = 17;
// Maybe nullity checks, if these are objects not primitives!
hash = hash * 23 + Zoom.GetHashCode();
hash = hash * 23 + X.GetHashCode();
hash = hash * 23 + Y.GetHashCode();
return hash;
}
}
xor
哈希的問題是:
X
等於Y
那么你的散列將只是 Zoom,因為那么X ^ Y = X ^ X = 0
成立xor
是一個對稱運算符,它將為對象生成完全相同的哈希值[Zoom = 3, X = 5, Y = 7]
, [Zoom = 3, X = 7, Y = 5]
, [Zoom = 7, X = 5, Y = 3]
等等。這些事實使異或方法更有可能引起沖突。
除了 Jons 的帖子,考慮使用unchecked
上下文,以明確忽略溢出。 因為就像MSDN說的:
如果既未使用
checked
也unchecked
使用unchecked
則常量表達式在編譯時使用默認的溢出檢查,即已檢查。 否則,如果表達式是非常量的,則運行時溢出檢查取決於其他因素,例如編譯器選項和環境配置。
因此,雖然通常不會檢查溢出,但它可能會在某些環境中或使用某些編譯器選項構建時失敗。 但在這種情況下,您希望明確不檢查這些溢出。
更新:
順便說一句: someInt.GetHashCode()
返回someInt
。 像這樣,它當然是最快的,並且是沒有單次沖突的完美散列分布。 你還會如何將 int 映射到 int-hash? :) 所以我想說的是:你的第一種方法:
return (Zoom + X + Y).GetHashCode();
和你的第二個:
return Zoom.GetHashCode() + X.GetHashCode() + Y.GetHashCode();
完全一樣。 您甚至不必調用GetHashCode
並且兩者很可能會發生沖突。 可能比xor
方法更糟糕,如果您很可能對所有三個整數都有小整數值。
更新 2:
正如我在 ChaosPandions 帖子的評論中所寫:如果您只有這三個 int 值,並且X
、 Y
和Zoom
是相對較小的數字(小於 1000 或 10000),那么這可能也是一個很好的哈希生成器:
public int GetHashCode()
{
return (X << 16) ^ (Y << 8) ^ Zoom;
}
它只是分配散列值中的位(例如,為了可讀性,在大端中):
00000000 00000000 00000011 00110001 X = 817
00000000 00000000 00011011 11111010 Y = 7162
00000000 00000000 00000010 10010110 Zoom = 662
00000011 00110001 00000000 00000000 X << 16
00000000 00011011 11111010 00000000 Y << 8
00000000 00000000 00000010 10010110 Zoom
00000011 00101010 11111000 10010110 (X << 16) ^ (Y << 8) ^ Zoom
您問題中的兩種實現都不理想。 例如,對於{ Zoom=1, X=2, Y=3 }
, { Zoom=2, X=3, Y=1 }
, { Zoom=3, X=1, Y=2 }
等等。
我通常使用這樣的東西:
public override int GetHashCode()
{
// 269 and 47 are primes
int hash = 269;
hash = (hash * 47) + Zoom.GetHashCode();
hash = (hash * 47) + X.GetHashCode();
hash = (hash * 47) + Y.GetHashCode();
return hash;
}
(根據記憶,我認為 C# 編譯器在為匿名類型生成GetHashCode
方法時使用了類似的方法。)
我實際上發現這非常有效。
public override int GetHashCode ()
{
return Zoom.GetHashCode() ^ X.GetHashCode() ^ Y.GetHashCode();
}
我知道這個問題有點老了,但現在你可以使用 System.HashCode 類輕松創建哈希碼
https://docs.microsoft.com/en-us/dotnet/api/system.hashcode.combine?view=netcore-3.1
在這種特定情況下,它看起來像
public override int GetHashCode()
{
return HashCode.Combine(Zoom, X, Y);
}
public override int GetHashCode ()
{
return (Zoom.ToString() + "-" + X.ToString() + "-" + Y.ToString()).GetHashCode();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.