2009-07-02 6 views
5

基本的に、私はこれまで、次のいる:複雑な等価性のためにObject.GetHashCode()を実装するにはどうすればよいですか?

class Foo { 
    public override bool Equals(object obj) 
    { 
     Foo d = obj as Foo ; 
     if (d == null) 
      return false; 

     return this.Equals(d); 
    } 

    #region IEquatable<Foo> Members 

    public bool Equals(Foo other) 
    { 
     if (this.Guid != String.Empty && this.Guid == other.Guid) 
      return true; 
     else if (this.Guid != String.Empty || other.Guid != String.Empty) 
      return false; 

     if (this.Title == other.Title && 
      this.PublishDate == other.PublishDate && 
      this.Description == other.Description) 
      return true; 

     return false; 
    } 
} 

ので、問題はこれです:私は、一意の識別子である非必須フィールドGuidを、持っています。これが設定されていない場合は、2つのオブジェクトが等しいかどうかを判断する試みとして、精度の低いメトリックに基づいて同等性を判断する必要があります。これはうまく動作しますが、それはGetHashCode()汚いです...どうすればいいですか?素朴な実装は、次のようなものになります。

public override int GetHashCode() { 
    if (this.Guid != String.Empty) 
     return this.Guid.GetHashCode(); 

    int hash = 37; 
    hash = hash * 23 + this.Title.GetHashCode(); 
    hash = hash * 23 + this.PublishDate.GetHashCode(); 
    hash = hash * 23 + this.Description.GetHashCode(); 
    return hash; 
} 

しかし、2種類のハッシュの衝突の可能性はありますか?確かに、私はそれが1 in 2 ** 32であるとは思わないでしょう。これは悪い考えですか、もしそうなら、どうすればいいでしょうか?

+0

ハッシュアルゴリズムが均等であることよりも等価アルゴリズムに合致することが重要です。ハッシュの目的は、ハッシュテーブル内でまともな分布を得ることだけであることを覚えておいてください。 1つの特定のバケツに大規模に歪んでいない限り、オッズはうまくいくでしょう。あなたが懸念している場合は、あなたのオブジェクトの消費者が遭遇する可能性のある妥当なシナリオを選びます。たとえば、合理的であれば数百個を辞書に入れておきます。結果。 –

+0

私が実際に見たことのあるものは〜200でしたが、典型的な使用は<30であり、あなたはおそらく正しいでしょう。 –

+1

ヘックは30項目以下で、リンクリストの線形検索はおそらく合理的に実行可能です。常に0のハッシュコードを返すことができ、衝突の可能性が100%あり、許容できるパフォーマンスを得ることができます。ハッシュコードの分布が良好であるという点は、辞書のサイズが大きくなるとパフォーマンスが向上することです。テーブルに小さな数のアイテムしか置かない場合は、厄介なディストリビューションを持ち、良い結果を得ることができます。 –

答えて

4

あなたが使用する方法に問題があるとは思われません。ハッシュの衝突について「あまりにも多く」心配しているのは、ほとんどの場合、問題を過度に考えていることを示しています。ハッシュが異なる可能性が高い限り、あなたはうまくいくはずです。

最終的には、ほとんどの時間オブジェクトがタイトルと公開日(書籍?)に基づいて区別できると思っているのであれば、とにかくDescriptionをあなたのハッシュから除外することを検討したいと思うかもしれません。

ハッシュ関数のGUIDを無視して、Equals実装でのみ使用して、ハッシュクラッシュの可能性のある(?)ケースを明確にすることもできます。

+0

明らかにGUIDが存在すれば、任意のタイトル文字列よりも早くハッシュする可能性が高いため、実現可能なパフォーマンスの最適化が可能です。 – jerryjvl

+0

説明は等価(したがって、ハッシュコード)に含める必要があります –

+0

ああ、レコードのRSSアイテム。 –

7

非常に簡単なhash code method for custom classesは、それぞれのフィールドのハッシュコードをビットごとにXORすることです。これは、このような単純なことができます:link aboveから

int hash = 0; 
hash ^= this.Title.GetHashCode(); 
hash ^= this.PublishDate.GetHashCode(); 
hash ^= this.Description.GetHashCode(); 
return hash; 

XORは、以下の素敵な性質を持っている:それは計算順序に依存しない

  • ビットを「無駄にしません」。いずれかのコンポーネントで1ビットでも変更すると、最終的な値が変更されます。
  • これは、素早く、最も基本的なコンピュータでさえ、1サイクルです。
  • 均一な分布を維持します。あなたが結合する2つの部分が均等に分散されている場合は、その組み合わせも同様です。つまり、ダイジェストの範囲をより狭い帯域に崩壊させる傾向はありません。

あなたが排他的論理和演算時に重複した値が互いに相殺されますよう、あなたのフィールドに重複する値を持つことが予想される場合XORはうまく動作しません。この場合、問題ではない3つの無関係なフィールドを一緒にハッシュしているからです。

+7

XORは計算の順番にも左右されません。両刃の剣です...同じタイプの複数のフィールド(たとえば2つの日付)を持つオブジェクトがある場合、これらのオブジェクトがスワップされるとオブジェクトは同じに見えます'ハッシュに。 – jerryjvl

関連する問題