2011-02-20 43 views
8

我有兩個字符串,該ID喜歡用它們作爲字典的鍵,但即時通訊有點懶創建另一個對象,計算串等的哈希碼兩個字符串的字典重點

所以不是說,我可以得到兩個字符串的hashcode,添加它們並使用結果作爲Dictionary的關鍵字?

有可能導致碰撞?對?。

任何想法?

+0

哪個.NET版本,你有嗎? –

回答

19

我有兩個字符串,如以 該ID使用它們作爲字典鍵,但即時通訊 有點懶創建另一個對象

在.NET 4.0中,你可以使用Tuple<T1, T2>類作爲鍵,T1和T2 =字符串。

我可以得到兩個字符串 的散列碼,添加它們並使用結果作爲詞典的 關鍵?

公式Tuple<T1, T2>用來組合哈希碼是一樣的東西(不記錄或保證不變):((h1 << 5) + h1)^h2,這應該是足夠體面的你的目的。順便說一句,天真添加通常不是組合散列碼的最佳方式。

有可能造成碰撞? 對不對?

這總是可能的,即使是單個字符串。有比32位整數更多的字符串。

3

使用一個元組:

var dict = new Dictionary<Tuple<string,string>,SomeType>(); 
dict.Add(Tuple.Create("Hello","World"), new SomeType()); 
10

如果你在.NET 4中,你可以使用Tuple類:

Dictionary<Tuple<string, string>, TValue> dict = new ... 

如果你不是在.NET 4中,需要創建你自己的類型來保存這個。

您可以使用KeyValuePair結構,但它繼承了基本值類型的相關方法,因此嚴重依賴於反射。這會對性能產生影響(見答案的底部。)

鍵值對:

Dictionary<KeyValuePair<string, string>, TValue> dict = new ... 

這裏的一般類型,你可以使用,如果你不想做飯了自己:

public struct SimpleTuple<TValue1, TValue2> 
{ 
    private readonly TValue1 _Value1; 
    private readonly TValue2 _Value2; 

    public SimpleTuple(TValue1 value1, TValue2 value2) 
    { 
     _Value1 = value1; 
     _Value2 = value2; 
    } 

    public TValue1 Value1 { get { return _Value1; } } 
    public TValue2 Value2 { get { return _Value2; } } 

    public int GetHashCode() 
    { 
     unchecked 
     { 
      int result = 37; 

      result *= 23; 
      if Value1 != null) 
       result += Value1.GetHashCode(); 

      result *= 23; 
      if (Value2 != null) 
       result += Value2.GetHashCode(); 

      return result; 
     } 
    } 

    public override bool Equals(object obj) 
    { 
     if (obj == null) return false; 
     if (obj.GetType() != typeof(SimpleTuple<TValue1, TValue2>)) 
      return false; 

     var other = (SimpleTuple<TValue1, TValue2>)obj; 
     return Equals(other.Value1, Value1) && Equals(other.Value2, Value2); 
    } 
} 

當然,KeyValuePair也適用於.NET 4.0,正如 不好。

至於碰撞,這取決於你的意思。散列表(字典在內部使用散列表結構)始終有可能發生關鍵衝突,但這就是比較起作用的地方。如果兩個不同的鍵生成相同的哈希碼,則字典類將比較鍵與鍵以查看它們是否真的是相同的值,或者只是生成相同的哈希碼。

背後,爲什麼一個哈希表總會有衝突的可能性的推理是最好用pidgeonhole principle (Wikipedia)描述。

這意味着,如果兩個不同的密鑰會引起衝突,它不會是一個問題,他們都會被保存,用正確的價值觀,在字典中。

當然,如果您創建兩次相同的密鑰,字典會將其計爲同一個密鑰,並且無法添加新值或覆蓋現有密鑰(具體取決於您如何添加該值。 )

這將在重複鍵拋出一個異常:

dict.Add(key, value); 

這將增加,或覆蓋現有:

dict[key] = value; 

在迴應阿尼的評論,我寫了下面簡單的測試腳本LINQPad。輸出是:

 
KeyValuePair: 975ms 
MyKeyValuePair: 52ms

腳本:

void Main() 
{ 
    const int iterations = 10 * 1000 * 1000; 

    // JIT preheat 
    Test1(1); 
    Test2(1); 

    Stopwatch sw = Stopwatch.StartNew(); 
    Test1(iterations); 
    sw.Stop(); 
    Debug.WriteLine("KeyValuePair: " + sw.ElapsedMilliseconds + "ms"); 

    sw = Stopwatch.StartNew(); 
    Test2(iterations); 
    sw.Stop(); 
    Debug.WriteLine("MyKeyValuePair: " + sw.ElapsedMilliseconds + "ms"); 
} 

public static void Test1(int iterations) 
{ 
    for (int index = 0; index < iterations; index++) 
    { 
     var kvp = new KeyValuePair<int, int>(index, index); 
     kvp.GetHashCode(); 
    } 
} 

public static void Test2(int iterations) 
{ 
    for (int index = 0; index < iterations; index++) 
    { 
     var kvp = new MyKeyValuePair<int, int>(index, index); 
     kvp.GetHashCode(); 
    } 
} 

public struct MyKeyValuePair<TKey, TValue> 
{ 
    private readonly TKey _Key; 
    private readonly TValue _Value; 

    public MyKeyValuePair(TKey key, TValue value) 
    { 
     _Key = key; 
     _Value = value; 
    } 

    public TKey Key { get { return _Key; } } 
    public TValue Value { get { return _Value; } } 

    public int GetHashCode() 
    { 
     unchecked 
     { 
      int result = 37; 

      result *= 23; 
      if (Key != null) 
       result += Key.GetHashCode(); 

      result *= 23; 
      if (Value != null) 
       result += Value.GetHashCode(); 

      return result; 
     } 
    } 

    public override bool Equals(object obj) 
    { 
     if (obj == null) return false; 
     if (obj.GetType() != typeof(MyKeyValuePair<TKey, TValue>)) 
      return false; 

     var other = (MyKeyValuePair<TKey, TValue>)obj; 
     return Equals(other.Key, Key) && Equals(other.Value, Value); 
    } 
} 
+0

使用KVPs作爲關鍵的經驗?我想知道性能會是什麼樣子,考慮到等同性和散列碼計算應該來自'System.ValueType',因爲它似乎並沒有覆蓋它們。 – Ani

+0

我沒有直接衡量這一點,但我從來沒有在字典是性能分析過程中的主要罪魁禍首的位置。它可能比用特定方法手動編碼類似的類型慢得多。讓我這樣做,然後回來編輯。 –

+0

@Ani,你現在在看,KeyValuePair是一個不錯的選擇。我會編輯我的答案。 –

3

簡單的解決方案,以及一個與.NET的所有版本。只需將字符串連接在一起即可。

var dictionary = new Dictionary<string, int>(); 
dictionary.Add("The meaning" + " of life, the universe, and everything", 42); 

當然,這僅與2串工作(儘管你可以在許多其他類型使用的ToString()),如果你不需要僅由兩個字符串中的一個來查找字典,但如果你有兩個很簡單。

+2

我會補充說,如果有兩個字符串中沒有包含某些字符,這種技術就可以工作。例如姓名和姓氏。他們都不應該有\ n(新行)。所以名稱\ nSurname是「足夠好」(注意,一些狡猾的黑客可以使用它來破解你的網站!這將是非常困難的,但不是不可能的)。考慮到許多系統都是基於C的,可能字符\ 0使用起來相當安全。 (或者你可以簡單地逃脫你使用分割字符串,例如字符的任何occurrance:Name.Replace(「|」,「||」)+「|」 +姓 – xanatos

相關問題