簡體   English   中英

我可以確定給定字符串的內置哈希值始終相同嗎?

[英]Can I be sure the built-in hash for a given string is always the same?

我得到這樣的字符串哈希:

string content = "a very long string";
int contentHash = content.GetHashCode();

然后我將哈希存儲到字典中作為到另一個ID的鍵映射。 這很有用所以我不必在默認字典哈希計算期間比較大字符串 ,但我可以通過鍵從字典中獲取ID。

我可以確定給定字符串的哈希值(“非常長的字符串”)將始終相同嗎?

我可以確定兩個不同的字符串不會具有相同的哈希值嗎?

另外,如果可能的話,為不同的字符串獲取相同的哈希的可能性有多大?

是的,它將是一致的,因為字符串是不可變的。 但是,我認為你在濫用字典。 您應該讓字典使用字符串作為鍵來獲取字符串的哈希值。 哈希不保證是唯一的,因此您可以用另一個密鑰覆蓋一個密鑰。

只是添加一些細節,以了解更改哈希碼的想法可能來自何處。

正如其他答案正確地說,特定字符串的哈希碼對於特定的運行時版本將始終是相同的。 由於性能原因,無法保證較新的運行時可能會使用不同的算法。

String類重寫object中的默認GetHashCode實現。

.NET中引用類型的默認實現是分配一個順序ID(由.NET內部保存)並將其分配給對象(對象堆存儲具有用於存儲此哈希碼的槽,它僅在第一次調用GetHashCode時分配)對於那個對象)。

因此,創建一個類的實例,為其分配一些值然后檢索哈希碼,然后使用相同的值集執行完全相同的序列將會產生不同的哈希碼。 這可能是導致一些人認為哈希碼可以改變的原因。 實際上,雖然它是一個類的實例,它被分配了一個哈希碼,一旦被分配,哈希碼就不會為該實例而改變。

編輯 :我剛剛注意到沒有一個答案直接引用你們每個人的問題(盡管我認為答案很明確)但只是為了整理: -

我可以確定給定字符串的哈希值(“非常長的字符串”)將始終相同嗎?

在您的使用中,是的。

我可以確定兩個不同的字符串不會具有相同的哈希值嗎?

不。兩個不同的字符串可能具有相同的哈希值

另外,如果可能的話,為不同的字符串獲取相同的哈希的可能性有多大?

概率非常低,因此從4G域中得到的散列非常隨機。

是的,那就是哈希碼的目的! 在運行時的不同版本之間不能保證相同。 有關MSDN的更多信息

正如其他人所指出的,散列將隨着時間的推移保持不變。 但是為什么要對字符串進行哈希處理,然后將其作為字典上的鍵? 哈希不保證是獨一無二的。 所以你比較可能是不正確的。 讓字典做它的工作。 我認為這個案例最合適的集合是HashSet

正如許多其他人所說,實現依賴於框架的版本,但它也取決於架構 string.GetHashCode()的實現在框架的x86和x64版本中是不同的,即使它們具有相同的版本號。

例如,如果您正在編寫客戶端/服務器或.net遠程處理類型的體系結構,並且希望使用字符串HashCode停止下載大型資源,則只有兩者具有相同的版本和位數時才能執行此操作。 否則你應該使用不同的哈希 - MD5,SHA等將正常工作。

Object.GetHashCode的文檔說明

如果兩個對象比較相等,則每個對象的GetHashCode方法必須返回相同的值。

因此,您可以保證給定字符串的哈希碼是相同的。 但是,您無法保證它是唯一的(可能有其他字符串具有相同的哈希碼)。

您不必猜測運行時或版本,只需使用我在業余時間創建的CaseInsensitiveStringComparer類(您可​​以將其傳遞給字典的構造函數,或者如果您使用的是.NET 3.5,則為HashSet):

/// <summary>
/// StringComparer that is basically the same as StringComparer.OrdinalIgnoreCase, except that the hash code function is improved and guaranteed not to change.
/// </summary>
public class CaseInsensitiveStringComparer : StringComparer
{
    /// <summary>
    /// Compares two strings, ignoring case
    /// </summary>
    /// <param name="x">First string</param>
    /// <param name="y">Second string</param>
    /// <returns>Compare result</returns>
    public override int Compare(string x, string y)
    {
        return StringComparer.OrdinalIgnoreCase.Compare(x, y);
    }

    /// <summary>
    /// Checks if two strings are equal, ignoring case
    /// </summary>
    /// <param name="x">First string</param>
    /// <param name="y">Second string</param>
    /// <returns>True if strings are equal, false if not</returns>
    public override bool Equals(string x, string y)
    {
        return Compare(x, y) == 0;
    }

    /// <summary>
    /// Gets a hash code for a string, ignoring case
    /// </summary>
    /// <param name="obj">String to get hash code for</param>
    /// <returns>Hash code</returns>
    public override int GetHashCode(string obj)
    {
        if (obj == null)
        {
            return 0;
        }
        int hashCode = 5381;
        char c;
        for (int i = 0; i < obj.Length; i++)
        {
            c = obj[i];
            if (char.IsLower(c))
            {
                c = char.ToUpperInvariant(c);
            }
            hashCode = ((hashCode << 5) + hashCode) + c;
        }
        return hashCode;
    }
}

字符串根據其內容進行哈希處理,因此,如果使用默認的GetHashCode,則哈希應該保持不變。

正如已經提到的,您可以確定參與其中的一個哈希值與它們基於內容進行哈希處理的哈希相同。 但是,您無法確定特定字符串是否會在此處提到的.NET框架的更高版本中進行相同的散列

所以我想說這個方法在內部用於應用程序時很好。 如果要將值保存到數據存儲,那么最好自行編寫函數以確保它在不同版本之間保持一致。

我可以確定給定字符串的哈希值(“非常長的字符串”)將始終相同嗎?

我可以確定兩個不同的字符串不會具有相同的哈希值嗎?

沒有

鑒於存在無限數量的不同字符串,它們不可能為每個字符串分配不同的int(32位,可以表示多達40億)。

只有8個字符,tehre是2 ^ 60個不同的字符串。 這無限大於2 ^ 32。 當然,其中一些字符串的哈希碼必須發生沖突。

具有相同哈希碼的兩個對象不必相等。 要確定使用equals方法。 這基本上是hashmap用來確定密鑰是否相等的策略。

Map.get(String key)

  • 計算密鑰的哈希碼
  • 使用modulo來確定哪個桶密鑰也屬於。
  • 循環通過該桶中的所有條目嘗試查找匹配的密鑰。
  • 找到密鑰匹配時返回條目的值。

作為旁注,隨着地圖獲得越來越多的元素,它將重新創建更多存儲桶並將所有舊條目放入新存儲桶中。 這有助於將存儲桶條目列表擴展為非常長的列表。 地圖需要許多帶有短列表的存儲桶。

Object.hashcode的javadoc有趣的閱讀 - 我在下面粘貼了一個片段。

 The equals method implements an equivalence relation:

* It is reflexive: for any reference value x, x.equals(x) should return true.
* It is symmetric: for any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
* It is transitive: for any reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) should return true.
* It is consistent: for any reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, provided no information used in equals comparisons on the object is modified.
* For any non-null reference value x, x.equals(null) should return false. 

類Object的equals方法實現了對象上最具辨別力的等價關系; 也就是說,對於任何參考值x和y,當且僅當x和y引用同一個對象時,此方法才返回true(x == y的值為true)。

這是過早優化的弊端的一個很好的例子。

您是否有分析器或基准測試的輸出告訴您同一個散列桶中的條目之間的字符串比較實際上是否會導致性能問題?

不這么認為。 只需將字符串本身用作Dictionary中的鍵。 這就是你應該如何使用它。

順便說一句,有不同的字符串遠遠不同的字符串,所以基本的邏輯告訴你,每個不同的字符串都不可能有不同的哈希碼。

暫無
暫無

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

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