2009-07-20 54 views
1

在.NET中,您需要Equals(object)和GetHashCode()是兼容的。但有時不能:當空間大於32位時,如何實現兼容GetHashCode的Equals方法?

public class GreaterThan32Bits 
{ 
    public int X { get; set; } 
    public int Y { get; set; } 
} 

因爲數據密度大於32位,和GetHashCode返回一個Int32,你將有3個解決方案(假設正確實現的GetHashCode):

  1. 避免代碼重複 丟棄不正確

    public override bool Equals(object other) 
    { 
        if(ReferenceEquals(null, other)) return false; 
        if(ReferenceEquals(this, other)) return true; 
        return this.GetHashCode() == other.GetHashCode(); 
    } 
    
  2. 實現從GetHashCode()方法

    public override bool Equals(object obj) 
    { 
        if(ReferenceEquals(null, other)) return false; 
        if(ReferenceEquals(this, other)) return true; 
        var other = obj as GreaterThan32Bits; 
        if(this.X == other.X) return this.Y == other.Y; 
        return false; 
    } 
    
  3. 單獨的Equals實現一個更大的精度GetHashCode64,所述overrided GetHashCode的(32個比特)將返回(INT)GetHashCode64(),並且等於將返回this.GetHashCode64()==其它.GetHashCode64()

你會實現哪一個?

第一種解決方案是 不精確 不正確,但更乾淨。第二個選項似乎很乾淨,但當類有更多屬性時會變得非常複雜。第三個選擇是妥協。

回答

1

我認爲你失蹤的關鍵是GetHashCode()不必返回唯一值。

它完全可以接受兩個不同的對象返回相同的GetHashCode。假設您將兩個對象添加到具有相同HashCode的HashSet中,那麼容器將首先使用GetHashCode來查找HashSet中的對象所在的位置,然後在所有匹配對象上使用equals來查找確切的對象。

顯然,如果每個對象都有一個唯一的哈希碼,它會更好。如果每個對象都返回相同的hashCode,那麼性能會很糟糕。

+1

重新閱讀您的問題:我認爲您應該保持GetHashCode和Equals方法分開。 GetHashCode()應該完全做到這一點:生成一個哈希碼。等於應該比較兩個對象的平等。 – 2009-07-20 23:21:34

+1

+1用於回答此問題的評論 – 2009-07-20 23:31:30

4

您的第一個執行是而不是正確。即使對象本身不相同,兩個對象的哈希碼也可能相同:這是哈希碼的本質。當兩個物體不等於

對象哈希碼可用於確定有用的,但以確定它們是否等於你將不得不調用.Equals()

對於GetHashCode()總是返回0的實現是合法的,但是如果將此類型的對象插入到各種類型的容器中,該實現可能不是非常有效。

您的選擇2是最佳選擇。將Equals()的實施與GetHashCode()分開是一個不錯的主意,因爲它們完成不同的事情。 Equals()必須返回true當且僅當兩個對象在所有方面都相等。爲此,您通常必須單獨檢查每個對象屬性。

+0

關於不正確性:我知道,這就是我稱之爲不精確的原因。 – 2009-07-20 23:28:00

2

嚴格地說,第一種解決方案不起作用。那麼這不是一個解決方案。

哈希的想法是完全不同的。 Int32足夠用於這個目的。

建議的GetHashCode()方法是

return X^Y; 

簡單,因爲它是。

編輯:Equals方法可以使用GetHashCode(),但只能在散列不同時返回false。無論如何都需要深入比較。

+0

問題不在於GetHashCode,而在於等於 – 2009-07-20 23:28:57

5

要求如下: 如果(a.Equals(b)中),然後a.GetHashCode()== b.GetHashCode()

不繞另一條路。

你不應該在GetHashCode()方面實現Equals()。對於GetHashCode有衝突是完全有效的,但Equals()不能返回誤報。

我建議這樣實現:

public override int GetHashCode() 
{ 
    return unchecked(this.X * p1 + this.Y * p2); 
} 

public override bool Equals(object obj) 
{ 
    var other = obj as GreaterThan32Bits; 
    // you must do the null test after the cast, otherwise the 
    // function crashes when obj is not a GreaterThan32Bits instance 
    if (ReferenceEquals(other, null)) return false; 
    return this.X == other.X && this.Y == other.Y; 
} 

其中P1和P2是大素數。這通常會產生一個很好的散列函數(很少有散列衝突 - >字典變得高效)。如果X和Y的值是獨立的(例如,你不希望像X = Y這樣的直線上有許多點),那麼即使像X^Y這樣簡單的東西也可以是一個很好的散列函數。

但是,如果實際上將類用作字典(或其他散列表)中的鍵,則只需要一個很好的散列函數。

事實上,在GetHashCode()中始終返回0並且僅實現Equals()是完全正確的。 詞典仍然可以正確使用鍵等對象,但效率不高。