2013-07-11 47 views
1

我有幾個頂點,我想把它放到一個Hashtable中。真正彼此接近的頂點被認爲是相同的頂點。我的C#頂點類看起來是這樣的:頂點的等號和散列碼

public class Vertex3D 
{ 
    protected double _x, _y, _z; 
    public static readonly double EPSILON = 1e-10; 

    public virtual double x 
    { 
     get { return _x;} 
     set { _x = value; } 
    } 

    public virtual double y 
    { 
     get { return _y; } 
     set { _y = value; } 
    } 

    public virtual double z 
    { 
     get { return _z; } 
     set { _z = value; } 
    } 

    public Vertex3D(double p1, double p2, double p3) 
    { 
     this._x = p1; 
     this._y = p2; 
     this._z = p3; 
    } 

    public override bool Equals(object obj) 
    { 
     var other = obj as Vertex3D; 

     if (other == null) 
     { 
      return false; 
     } 
     double diffx = this.x - other.x; 
     double diffy = this.y - other.y; 
     double diffz = this.z - other.z; 
     bool eqx = diffx > -EPSILON && diffx < EPSILON; 
     bool eqy = diffy > -EPSILON && diffy < EPSILON; 
     bool eqz = diffz > -EPSILON && diffz < EPSILON; 
     return eqx && eqy && eqz; 
    } 

    public override int GetHashCode() 
    { 
     return this.x.GetHashCode()^this.y.GetHashCode()^this.z.GetHashCode(); 
    } 

    public override string ToString() 
    { 
     return "Vertex:" + " " + x + " " + y + " " + z; 
    } 

現在可以說,我把下面的兩個頂點到字典(一本字典是不允許null鍵哈希表):

Dictionary<Vertex3D, Vertex3D> vertexList = new Dictionary<Vertex3D, Vertex3D>(); 
    Vertex3D v0 = new Vertex3D(0.000000000000000037842417475065449, -1, 0.00000000000000011646698526992202)); 
    Vertex3D v1 = new Vertex3D(0, -1, 0)); 
    vertexList.Add(v0, v0); 
    vertexList.Add(v1, v1); 

的問題是我的equals和hashcode的實現有問題。上述兩個頂點被認爲是相等的,因爲彼此之間的距離小於EPSILON。但他們不會返回相同的哈希碼。

如何正確實現equals和hashcode?

回答

1

哈希表需要等價類,但是您的Equals()不是可傳遞的。因此你不能爲此使用散列表。 (例如,如果你允許附近的物體通過四捨五入來比較相等的點,你就可以得到傳遞性和等價性類,但是這裏仍然會有任意的接近點,直到你的表示的精確度,的閾值,因此處於不同的等價類別中)

還有其他數據結構,例如八進制,它旨在加速查找附近的點。我建議你使用其中之一。

1

通常,只有在它們都引用同一個對象時,可變引用引用才應該被認爲是等價的。只有對不可變事物的引用應該使用任何其他的相等定義。如果Object包含虛擬函數以在兩個引用由不同對象持有的情況下測試等價性將會很有幫助,這兩個引用都不會暴露其引用可能會改變它的任何內容。不幸的是,即使有效不可變實例的可變類型模式非常普遍(幾乎所有不可變集合,例如,使用一個或多個可變類型對象(如數組)來保存其數據),但沒有標準模式用它進行等值測試。

如果要使用Object.Equals在字典中存儲頂點進行相等性測試,它應該是不可變的類型。或者,您可以定義一個用於字典的自定義IEqualityComparer<T>,但您應該知道Dictionary只應用於查找完美的匹配項。如果您希望能夠找到某個點的EPSILON內的任何點,則應該使用一個將四捨五入的值映射到精確值的列表(值應該四捨五入爲2的冪,這至少是epsilon的兩倍) 。如果從點的某些或全部座標中增加或減去EPSILON會導致其不同,則該點應該包含在字典中,並按照每種可能的方式進行四捨五入。