簡體   English   中英

實現此復合 GetHashCode() 的最佳方法是什么

[英]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說的:

如果既未使用checkedunchecked使用unchecked則常量表達式在編譯時使用默認的溢出檢查,即已檢查。 否則,如果表達式是非常量的,則運行時溢出檢查取決於其他因素,例如編譯器選項和環境配置。

因此,雖然通常不會檢查溢出,但它可能會在某些環境中或使用某些編譯器選項構建時失敗。 但在這種情況下,您希望明確不檢查這些溢出。

更新:

順便說一句: someInt.GetHashCode()返回someInt 像這樣,它當然是最快的,並且是沒有單次沖突的完美散列分布。 你還會如何將 int 映射到 int-hash? :) 所以我想說的是:你的第一種方法:

return (Zoom + X + Y).GetHashCode();

和你的第二個:

return Zoom.GetHashCode() + X.GetHashCode() + Y.GetHashCode();

完全一樣。 您甚至不必調用GetHashCode並且兩者很可能會發生沖突。 可能比xor方法更糟糕,如果您很可能對所有三個整數都有小整數值。

更新 2:

正如我在 ChaosPandions 帖子的評論中所寫:如果您只有這三個 int 值,並且XYZoom是相對較小的數字(小於 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM