1

.NET では、Equals(object) と GetHashCode() に互換性がある必要があります。ただし、次のことができない場合があります。

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() とは別に Equals を実装する

    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. より高い精度の GetHashCode64 を実装すると、オーバーライドされた GetHashCode (32 ビット) は (int)GetHashCode64() を返し、Equals は this.GetHashCode64() == other.GetHashCode64() を返します。

どれを実装しますか?

最初の解決策は不正確ですが、よりクリーンです。2 番目のオプションはきれいに見えますが、クラスのプロパティが増えると非常に複雑になります。3 番目のオプションは妥協です。

4

4 に答える 4

5

要件は次のとおりです。if (a.Equals(b)), then 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() のみを実装しても問題ありません。ディクショナリは、キーなどのオブジェクトでも正しく機能しますが、非効率的です。

于 2009-07-20T23:29:14.720 に答える
4

最初の実装は正しくありません。オブジェクト自体は等しくなくても、2 つのオブジェクトのハッシュ コードは等しい場合があります。これがハッシュ コードの本質です。

オブジェクト ハッシュ コードは、2 つのオブジェクトが等しくない場合を判断するのに役立ちますが、それら等しいかどうかを判断するには、 を呼び出す必要があります.Equals()

常に 0 を返す実装GetHashCode()は有効ですが、そのタイプのオブジェクトがさまざまなタイプのコンテナーに挿入される場合、あまり効率的ではない可能性があります。

オプション 2 が最適です。Equals()の実装を とは別にしておくことをお勧めしGetHashCode()ます。これらはまったく異なることを行うからです。2 つのオブジェクトがすべての点で等しい場合にのみEquals()返さなければなりません。trueこれを行うには、通常、各オブジェクト プロパティを個別に確認する必要があります。

于 2009-07-20T23:19:51.153 に答える
2

厳密に言えば、最初の解決策は機能しません。それでは解決にはなりません。

ハッシュの考え方はまったく異なります。その目的には Int32 で十分です。

推奨される GetHashCode() は

return X ^ Y;

そのままです。

EDIT : Equals メソッドは GetHashCode() を使用できますが、ハッシュが異なる場合にのみ false を返します。とにかく深い比較が必要です。

于 2009-07-20T23:21:13.550 に答える
1

あなたが見逃しているキーは、 GetHashCode() が一意の値を返す必要がないことだと思います。

2 つの異なるオブジェクトが同じ GetHashCode を返すことはまったく問題ありません。同じ HashCode を持つ 2 つのオブジェクトを HashSet に追加すると、コンテナーは最初に GetHashCode を使用して HashSet 内のおおよその場所を見つけ、次に一致するすべてのオブジェクトで equals を使用して正確なオブジェクトを見つけます。

各オブジェクトが一意のハッシュ コードを持っている場合は明らかに優れています。すべてのオブジェクトが同じ hashCode を返した場合、パフォーマンスは最悪になります。

于 2009-07-20T23:19:50.467 に答える