web-dev-qa-db-ja.com

HashSetで重複アイテムの挿入が可能-C#

この種の質問は、noobの質問のように思えますが、この質問に対する具体的な答えは見つかりませんでした。

私はこのクラスを持っています:

public class Quotes{ 
    public string symbol; 
    public string extension
}

そしてこれを使用しています:

HashSet<Quotes> values = new HashSet<Quotes>();

ただし、同じQuotesオブジェクトを複数回追加できます。たとえば、Quotesオブジェクトには「A」に等しい「symbol」と「= n」に等しい「extension」があり、このQuotesオブジェクトはHashSetに複数回表示されます(デバッグモードでHashsetを表示)。私は電話したときに

values.Add(new Quotes(symb, ext));

同じsymbとextを使用すると、「false」が返され、要素は追加されません。 HashSetが新しいオブジェクトを追加するときにQuotesオブジェクトを比較することに関係があると感じています。どんな助けも大歓迎です!

38
jpints14

同じ値で新しいQuotesを作成していると思います。この場合、それらは等しくありません。等しいと見なされる場合は、EqualsメソッドとGetHashCodeメソッドをオーバーライドします。

public class Quotes{ 
    public string symbol; 
    public string extension

    public override bool Equals(object obj)
    {
        Quotes q = obj as Quotes;
        return q != null && q.symbol == this.symbol && q.extension == this.Extension;
    }

    public override int GetHashCode()
    {
        return this.symbol.GetHashCode() ^ this.extension.GetHashCode();
    }
}
50
Kendall Frey

同じsymbとextでvalues.Add(new Quotes(symb, ext));を呼び出すと、 'false'が返され、要素は追加されないと考えていました。

これはそうではありません。

HashSetはGetHashCodeEqualsを使用してオブジェクトの同等性を判断します。現時点では、Quotesでこれらのメソッドをオーバーライドしていないため、デフォルトのSystem.Objectの参照等価性が使用されます。新しいQuoteを追加するたびに、それは一意のオブジェクトインスタンスになるため、HashSetはそれを一意のオブジェクトとして認識します。

Object.EqualsObject.GetHashCodeをオーバーライドすると、期待どおりに機能します。

19
Reed Copsey

HashSetはまず、GetHashCodeによって計算されるハッシュに基づいてエントリを比較します。
デフォルトの実装は、オブジェクト自体に基づいてハッシュコードを返します(各インスタンス間で異なります)。

ハッシュが同じ場合(インスタンスに基づくハッシュでは非常にありそうにない場合)にのみ、Equalsメソッドが呼び出され、2つのオブジェクトを確実に比較するために使用されます。

オプションが必要です:

  • 引用符を構造体に変更する
  • GetHashCodeと引用符で等しいをオーバーライド

例:

 public override int GetHashCode()
 {
    return (this.symbol == null ? 0 : this.symbol.GetHashCode())
       ^ (this.extension == null ? 0 : this.extension.GetHashCode());
 }
 public override bool Equals(object obj)
 {
    if (Object.ReferenceEquals(this, obj))
      return true;

    Quotes other = obj as Quotes;
    if (Object.ReferenceEquals(other, null))
      return false;

    return String.Equals(obj.symbol, this.symbol)
        && String.Equals(obj.extension, this.extension);
 }
6
Matthias

ケンドールの答えの何かを修正したかっただけです(奇妙な理由でコメントできません)。

return this.symbol.GetHashCode() ^ this.extension.GetHashCode();

Xor関数は、特に両方が同じタイプである場合(シンボル==拡張のあるすべてのオブジェクトが0にハッシュされるため)、2つのハッシュを結合する例外的に衝突しやすい方法であることに注意してください。それらが同じタイプではない場合、または互いに等しくなる可能性が低い場合でも、これは悪い習慣であり、それに慣れると、異なるアプライアンスで問題が発生する可能性があります。

代わりに、1つのハッシュに小さな素数を乗算し、2番目のハッシュを追加します(例:

return 3 * this.symbol.GetHashCode() + this.extension.GetHashCode();
4
leetrobot

私はこれが少し遅いことを知っていますが、同じ問題にぶつかり、特に多くのレコードがある場合、選択した答えを実装しているときに許容できないパフォーマンスヒットを見つけました。

これをHashsetとTupleを使用して2ステップのプロセスに変換し、最終的にSelectを介して変換する方がはるかに高速であることがわかりました。

public class Quotes{ 
    public string symbol; 
    public string extension
}

var values = new HashSet<Tuple<string,string>>();

values.Add(new Tuple<string,string>("A","=n"));
values.Add(new Tuple<string,string>("A","=n"));

// values.Count() == 1

values.Select (v => new Quotes{ symbol = v.Item1, extension = v.Item2 });
2
user1265146
Quotes q = new Quotes() { symbol = "GE", extension = "GElec" };
values.Add(q);
values.Add(q);

..は同じインスタンスを2回追加し、2回目にfalseを返します。

values.Add(new Quotes() { symbol = "GE", extension = "GElec" });
values.Add(new Quotes() { symbol = "GE", extension = "GElec" });

..は、パブリックフィールドに同じ値を持つ2つの異なるインスタンスを追加しています。

他の場所で述べたように、EqualsとGetHashCodeをオーバーライドするとこれが修正されます。

public class Quotes { 
    public string symbol; 
    public string extension;

    public override bool Equals(object obj) {
        if (!(obj is Quotes)) { return false; }
        return (this.symbol == ((Quotes)obj).symbol) && 
               (this.extension == ((Quotes)obj).extension);
    }

    public override int GetHashCode() {
        return (this.symbol.GetHashCode()) ^ (this.extension.GetHashCode());
    }
} 

コードをステップデバッグすると、values.AddはQuotes.EqualsとQuotes.GetHashCodeの両方を呼び出します。

2
Joshua Honig