簡體   English   中英

我可以依賴GetHashCode()的值來保持一致嗎?

[英]Can I depend on the values of GetHashCode() to be consistent?

假設使用相同的字符串值,GetHashCode()的返回值是否保證一致? (C#/ ASP.NET)

我今天將我的代碼上傳到服務器,令我驚訝的是我不得不重新索引一些數據,因為我的服務器(win2008 64位)與台式機相比返回了不同的值。

如果我沒有弄錯,GetHashCode在給定相同值的情況下是一致的,但不保證在不同版本的框架中保持一致。

從String.GetHashCode()上的MSDN文檔:

GetHashCode的行為取決於其實現,該實現可能從公共語言運行庫的一個版本更改為另一個版本。 可能發生這種情況的原因是為了提高GetHashCode的性能。

我有一個類似的問題,我在數據庫表中填充了依賴於String.GetHashCode(不是最好的主意)的信息,當我升級服務器時,我正在研究x64,我注意到我從String.GetHashCode得到的值是與表中已有的內容不一致。 我的解決方案是使用我自己的GetHashCode版本,它在x86框架上返回與String.GetHashCode相同的值。

這是代碼,不要忘記編譯“允許不安全的代碼”:

    /// <summary>
    /// Similar to String.GetHashCode but returns the same as the x86 version of String.GetHashCode for x64 and x86 frameworks.
    /// </summary>
    /// <param name="s"></param>
    /// <returns></returns>
    public static unsafe int GetHashCode32(string s)
    {
        fixed (char* str = s.ToCharArray())
        {
            char* chPtr = str;
            int num = 0x15051505;
            int num2 = num;
            int* numPtr = (int*)chPtr;
            for (int i = s.Length; i > 0; i -= 4)
            {
                num = (((num << 5) + num) + (num >> 0x1b)) ^ numPtr[0];
                if (i <= 2)
                {
                    break;
                }
                num2 = (((num2 << 5) + num2) + (num2 >> 0x1b)) ^ numPtr[1];
                numPtr += 2;
            }
            return (num + (num2 * 0x5d588b65));
        }
    }

實現取決於框架的版本,但也取決於體系結構 string.GetHashCode()的實現在框架的x86和x64版本中是不同的,即使它們具有相同的版本號。

    /// <summary>
    /// Default implementation of string.GetHashCode is not consistent on different platforms (x32/x64 which is our case) and frameworks. 
    /// FNV-1a - (Fowler/Noll/Vo) is a fast, consistent, non-cryptographic hash algorithm with good dispersion. (see http://isthe.com/chongo/tech/comp/fnv/#FNV-1a)
    /// </summary>
    private static int GetFNV1aHashCode(string str)
    {
        if (str == null)
            return 0;
        var length = str.Length;
        // original FNV-1a has 32 bit offset_basis = 2166136261 but length gives a bit better dispersion (2%) for our case where all the strings are equal length, for example: "3EC0FFFF01ECD9C4001B01E2A707"
        int hash = length;
        for (int i = 0; i != length; ++i)
            hash = (hash ^ str[i]) * 16777619;
        return hash;
    }

此實現可能比之前發布的不安全實現慢。 但更簡單,更安全。

我想知道32位和64位操作系統之間是否存在差異,因為我確定我的服務器和家用計算機都運行相同版本的.NET

我一直厭倦了使用GetHashCode(),對我來說,簡單地使用自己的哈希算法可能是一個好主意。 好吧,至少我最終寫了一個快速的重新索引.aspx頁面因為它。

您是否正在運行Win2008 x86作為桌面? 因為Win2008包含版本2.0.50727.1434 ,這是Vista RTM中包含的2.0的更新版本。

不能直接回答你的問題,Jonas已經回答得很好,但如果你擔心哈希中的平等測試,這可能會有所幫助

根據我們的測試,根據您對哈希碼的要求,在C#中,對於Equality操作,哈希碼不需要是唯一的。 例如,請考慮以下事項:

我們需要重載equals運算符,因此我們的對象的GetHashCode函數變得易失和無狀態,並直接從數據中獲取,因此在應用程序的一個位置我們需要確保查看對象如果它來自同一數據等同於另一個對象,而不僅僅是它是相同的引用。 我們唯一的數據標識符是Guids。

equals運算符很容易滿足,因為我們剛檢查了記錄的Guid(在檢查null之后)。

不幸的是,HashCode數據大小(作為int)取決於操作系統,而在我們的32位系統上,哈希碼將是32位。 在數學上,當我們覆蓋GetHashCode函數時,不可能從大於32位的guid生成唯一的哈希碼(從相反的角度來看,如何將32位整數轉換為guid?)。

然后我們做了一些測試,我們將Guid作為一個字符串並返回Guid的HashCode,它幾乎總是在我們的測試中返回一個唯一的標識符,但並非總是如此。

然而,我們注意到,當一個對象在散列集合對象(散列表,字典等)中時,當2個對象不是唯一但是它們的散列碼是,則散列碼僅用作第一個選項查找,如果有非- 使用的是唯一的哈希碼, 相等運算符總是用作退化等級的平等

正如我所說,這可能與您的情況有關,也可能與您的情況無關,但如果它是一個方便的提示。

UPDATE

為了演示,我們有一個Hashtable:

密鑰:對象A(哈希碼1),值對象A1

密鑰:對象B(哈希碼1),值對象B1

密鑰:對象C(哈希碼1),值對象C1

密鑰:對象D(哈希碼2),值對象D1

密鑰:對象E(哈希碼3),值對象E1

當我使用對象A的鍵調用對象的哈希表時,對象A1將在2步后返回,調用哈希碼1,然后對密鑰對象進行相等檢查,因為沒有哈希碼1的唯一鍵

當我用對象D的鍵調用對象的哈希表時,對象D1將在1步之后返回,即哈希查找

然而,我們注意到,當一個對象在散列集合對象(散列表,字典等)中時,當2個對象不是唯一但是它們的散列碼是,則散列碼僅用作第一個選項查找,如果有非 - 使用的是唯一的哈希碼,相等運算符總是用作退化等級的平等。

這是散列查找的工作方式,對嗎? 每個桶包含具有相同哈希碼的項目列表。

因此,為了在這些條件下找到正確的項目,使用值相等比較進行線性搜索。

如果您的哈希實現實現了良好的分發,則不需要此搜索,即每個桶一個項目。

我的理解是否正確?

我不得不說......你不能依賴它。 例如,如果我通過c#的md5哈希碼運行file1並復制nd將同一個文件粘貼到一個新目錄...哈希代碼變得與眾不同甚至很難它是同一個文件。 顯然它是相同的.net版本,同樣的一切。 唯一改變的是路徑。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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