web-dev-qa-db-ja.com

両方の文字列が交換可能な場合、2つの文字列を含む構造のGetHashCodeをどのように実装しますか

私はC#で構造を持っています:

_public struct UserInfo
{
   public string str1
   {
     get;
     set;
   }

   public string str2
   {
     get;
     set;
   }   
}
_

唯一のルールは、UserInfo(str1="AA", str2="BB").Equals(UserInfo(str1="BB", str2="AA"))

この構造のGetHashCode関数をオーバーライドする方法は?

66
Graviton

[〜#〜] msdn [〜#〜]

ハッシュ関数には次のプロパティが必要です。

  • 2つのオブジェクトが等しいと比較される場合、各オブジェクトのGetHashCodeメソッドは同じ値を返す必要があります。ただし、2つのオブジェクトが等しく比較されない場合、2つのオブジェクトのGetHashCodeメソッドは異なる値を返す必要はありません。
  • オブジェクトのGetHashCodeメソッドは、オブジェクトのEqualsメソッドの戻り値を決定するオブジェクト状態に変更がない限り、常に同じハッシュコードを返す必要があります。これは、アプリケーションの現在の実行にのみ当てはまることに注意してください。アプリケーションを再度実行すると、異なるハッシュコードが返される可能性があります。
  • 最高のパフォーマンスを得るには、ハッシュ関数はすべての入力に対してランダムな分布を生成する必要があります。

それを正しい方法で考慮することは:

return str1.GetHashCode() ^ str2.GetHashCode() 

^は、他の可換演算に置き換えることができます

66
aku

Jon Skeetの答え を参照-^は良くありません。しばしば衝突ハッシュを生成します!

26
Tomáš Kafka
public override int GetHashCode()
{
    unchecked
    {
        return (str1 ?? String.Empty).GetHashCode() +
            (str2 ?? String.Empty).GetHashCode();
    }
}

( 'AA'、 'BB')と( 'BB'、 'AA')を明示的に同じにしたいが、(^ 「AA」、「AA」)および(「BB」、「BB」)が同じ(または、すべての等しいペア).

Nullの場合、これは既知の定数をすぐに返すのではなく、空の文字列で 'GetHashCode()'を実行するため、このソリューションでは「可能な限り高速」のルールが完全に順守されていませんが、明示的に測定しなくても多くのヌルを期待しない限り、違いが心配するほど大きくないという推測を危険にさらすために。

15
Jerry
  1. 一般的なルールとして、クラスのハッシュコードを生成する簡単な方法は、ハッシュコードの生成に参加できるすべてのデータフィールドをXORすることです(他の人が指摘したようにnullをチェックするよう注意してください)。これは、UserInfo( "AA"、 "BB")とUserInfo( "BB"、 "AA")のハッシュコードが同じであるという(人工的?)要件も満たしています。

  2. クラスの使用について推測できる場合は、おそらくハッシュ関数を改善できます。たとえば、str1とstr2が同じであることが一般的である場合、XORは適切な選択ではない場合があります。しかし、str1とstr2が、たとえば姓と名を表す場合、XORがおそらく適切な選択です。

これは明らかに実世界の例であることを意図していないが、それを指摘する価値があるかもしれない:-これはおそらく構造体の使用の悪い例です:構造体は通常値セマンティクスを持つべきであり、ここにケース。 -セッターを使用してプロパティを使用してハッシュコードを生成することも問題の原因です。

5
Joe

簡単な一般方法はこれを行うことです:

return string.Format("{0}/{1}", str1, str2).GetHashCode();

厳密なパフォーマンス要件がない限り、これは私が考えることができる最も簡単な方法であり、複合キーが必要なときにこの方法を頻繁に使用します。 nullのケースをうまく処理し、(一般的に)ハッシュ衝突を(m)引き起こしません。文字列に「/」が必要な場合は、予期しない別のセパレータを選択してください。

4

ReSharperが提案している線に沿って進むと:

public int GetHashCode()
{
    unchecked
    {
        int hashCode;

        // String properties
        hashCode = (hashCode * 397) ^ (str1!= null ? str1.GetHashCode() : 0);
        hashCode = (hashCode * 397) ^ (str2!= null ? str1.GetHashCode() : 0);

        // int properties
        hashCode = (hashCode * 397) ^ intProperty;
        return hashCode;
    }
}

397は、結果変数がオーバーフローし、ハッシュのビットをいくらか混合するのに十分なサイズの素数であり、ハッシュコードのより良い分布を提供します。それ以外の場合、397には同じ大きさの他の素数と区別する特別なものはありません。

3
public override int GetHashCode()   
{       
    unchecked      
    {           
        return(str1 != null ? str1.GetHashCode() : 0) ^ (str2 != null ? str2.GetHashCode() : 0);       
    }   
}
3
user11556

ああ、ゲイリー・シュトラーが指摘したように:

return str1.GetHashCode() + str2.GetHashCode();

オーバーフローする可能性があります。 Artemが提案したように長くキャストするか、未チェックのキーワードでステートメントを囲むことができます。

return unchecked(str1.GetHashCode() + str2.GetHashCode());
2
Grokys

これを試してください:

(((long)str1.GetHashCode()) + ((long)str2.GetHashCode())).GetHashCode()
1

多くの可能性。例えば。

return str1.GetHashCode() ^ str1.GetHashCode()

0
VolkerK

おそらくstr1.GetHashCode()+ str2.GetHashCode()のようなものでしょうか?または(str1.GetHashCode()+ str2.GetHashCode())/ 2?この方法では、str1とstr2がスワップされるかどうかに関係なく同じになります。

0
Mike Stone

それらをソートしてから連結します。

 return((str1.CompareTo(str2)<1)?str1 + str2:str2 + str1)
 .GetHashCode(); 
0
Steve Morgan

GetHashCodeの結果は次のようになります。

  1. できるだけ早く。
  2. 可能な限りユニーク。

それらを念頭に置いて、私は次のようなものに行きます:

if (str1 == null)
    if (str2 == null)
        return 0;
    else
       return str2.GetHashCode();
else
    if (str2 == null)
        return str1.GetHashCode();
    else
       return ((ulong)str1.GetHashCode() | ((ulong)str2.GetHashCode() << 32)).GetHashCode();

編集: nullを忘れました。コードが修正されました。

0