2015-07-03 95 views
4

在我開始,所有的代碼樣本這裏,我在Mono環境測試,並沒有在GetHashCode實現一個顯着的區別:在C#中實現GetHashCode。空值處理

string.Empty.GetHashCode(); // returns 0 in Mono 3.10 
string.Empty.GetHashCode(); // returns 757602046 in .NET 4.5.1 

我在我的基礎上通過@JonSkeet並在此SO Answer實施他還建議使用零散列代碼作爲NULL值(不知道該如何散列它們)。

我通常使用0作爲null的有效哈希碼 - 這與忽略該字段不同。

因此,有以下implementation(單聲道3.10):

public class Entity { 
    public int EntityID { get; set; } 
    public string EntityName { get; set; } 

    public override int GetHashCode() { 
     unchecked { 
      int hash = 15485863;  // prime number 
      int multiplier = 1299709; // another prime number 

      hash = hash * multiplier + EntityID.GetHashCode(); 
      hash = hash * multiplier + (EntityName != null ? EntityName.GetHashCode() : 0); 

      return hash; 
     } 
    } 
} 

這是很容易找到例如碰撞

var hash1 = new Entity { EntityID = 1337, EntityName = "" }.GetHashCode(); 
var hash2 = new Entity { EntityID = 1337, EntityName = null }.GetHashCode(); 

bool equals = hash1 == hash2; // true 

我可以用一些其他數字代替空值0,但它不會解決問題,還有一個機會,一些哈希(串),輸出會產生這樣的數字,我會得到另一個碰撞。

我的問題:如何使用上面的示例中的算法處理空值?

+4

您將始終有碰撞在哈希碼中。這就是哈希碼的本質。出於這個原因,你**總是**實現一個類的equals函數,該類生成一個哈希代碼來驗證返回相同哈希代碼的兩個對象是否真的相等。 – Nitram

+0

我的示例中的對象具有相同哈希代碼的不同狀態。我錯過了什麼嗎? –

+0

忽略這個建議,不要使用'0'來代替'null',而要使用別的東西,因爲'String.Empty'在單聲道上也給了你'0',而且@Nitram說你不能總是避免碰撞。 – Habib

回答

2

貴「的問題」這裏是您嘗試了get衝突散列碼。雖然這對於在大多數情況下使用哈希代碼進行查找的收集實現(例如HashSetDictionary)的查找性能來說是完美的,但這不起作用。

原因是哈希碼只是一個32位整數值,它代表的數據通常比較大(多個整數值,字符串等)。

所以哈希碼僅用於定義兩個對象可以相等。集合類使用哈希代碼來優化存儲對象的區域,並使用equals函數來查找兩個對象是否真的相同。出於這個原因,您應該始終爲您實施哈希代碼的類實現Equals函數。雖然這些類將回退到object的equals函數,但是實現IEquatable<T>接口以避免輸入任何類型的問題也是一個好主意(仍然覆蓋Object的默認equals方法!)

+1

現在我明白了,出於某種原因,我曾經認爲以'obj.GetHashCode()== this.GetHashCode()'(+ type/null checks)的方式實現'Equals'是個好主意,這就是爲什麼我在尋找無碰撞算法,但實際上,正如你所說的,32位整數散列並不能保證獨特性,因爲它在大多數情況下太小。謝謝 –

3

我的問題:如何從上面的例子中使用算法 來處理空值?

我不認爲問題是與null本身。問題在於,您正在使用GetHashCode進行平等,而這並不意味着這一點。 GetHashCode應該提供渴望正常分佈的哈希。

The docs say

兩個對象是相等的回報相同的散列碼。然而, 反之並非如此:相同的散列碼並不意味着對象 相等,因爲不同的(不相等的)對象可以具有相同的散列 代碼。

然後前進到指定的GetHashCode目的:

的哈希代碼是用於有效插入和查找在是基於哈希表 集合。

您應該實施IEquatable<Entity>,其中您實際定義了兩個實體的等價關係。當你在它的時候覆蓋!===

它想在這行:

public class Entity : IEquatable<Entity> 
{ 
    public int EntityId { get; set; } 
    public string EntityName { get; set; } 

    public bool Equals(Entity other) 
    { 
     if (ReferenceEquals(null, other)) return false; 
     if (ReferenceEquals(this, other)) return true; 
     return EntityId == other.EntityId && 
       string.Equals(EntityName, other.EntityName); 
    } 

    public override bool Equals(object obj) 
    { 
     if (ReferenceEquals(null, obj)) return false; 
     if (ReferenceEquals(this, obj)) return true; 
     if (obj.GetType() != this.GetType()) return false; 
     return Equals((Entity) obj); 
    } 

    public static bool operator ==(Entity left, Entity right) 
    { 
     return Equals(left, right); 
    } 

    public static bool operator !=(Entity left, Entity right) 
    { 
     return !Equals(left, right); 
    } 

    public override int GetHashCode() 
    { 
     unchecked 
     { 
      return (EntityId*397)^(EntityName != null ? EntityName.GetHashCode() : 0); 
     } 
    } 
}