この種の質問は、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オブジェクトを比較することに関係があると感じています。どんな助けも大歓迎です!
同じ値で新しい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();
}
}
同じsymbとextで
values.Add(new Quotes(symb, ext));
を呼び出すと、 'false'が返され、要素は追加されないと考えていました。
これはそうではありません。
HashSetはGetHashCode
とEquals
を使用してオブジェクトの同等性を判断します。現時点では、Quotes
でこれらのメソッドをオーバーライドしていないため、デフォルトのSystem.Object
の参照等価性が使用されます。新しいQuoteを追加するたびに、それは一意のオブジェクトインスタンスになるため、HashSetはそれを一意のオブジェクトとして認識します。
Object.Equals
とObject.GetHashCode
をオーバーライドすると、期待どおりに機能します。
HashSetはまず、GetHashCode
によって計算されるハッシュに基づいてエントリを比較します。
デフォルトの実装は、オブジェクト自体に基づいてハッシュコードを返します(各インスタンス間で異なります)。
ハッシュが同じ場合(インスタンスに基づくハッシュでは非常にありそうにない場合)にのみ、Equalsメソッドが呼び出され、2つのオブジェクトを確実に比較するために使用されます。
オプションが必要です:
例:
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);
}
ケンドールの答えの何かを修正したかっただけです(奇妙な理由でコメントできません)。
return this.symbol.GetHashCode() ^ this.extension.GetHashCode();
Xor関数は、特に両方が同じタイプである場合(シンボル==拡張のあるすべてのオブジェクトが0にハッシュされるため)、2つのハッシュを結合する例外的に衝突しやすい方法であることに注意してください。それらが同じタイプではない場合、または互いに等しくなる可能性が低い場合でも、これは悪い習慣であり、それに慣れると、異なるアプライアンスで問題が発生する可能性があります。
代わりに、1つのハッシュに小さな素数を乗算し、2番目のハッシュを追加します(例:
return 3 * this.symbol.GetHashCode() + this.extension.GetHashCode();
私はこれが少し遅いことを知っていますが、同じ問題にぶつかり、特に多くのレコードがある場合、選択した答えを実装しているときに許容できないパフォーマンスヒットを見つけました。
これを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 });
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の両方を呼び出します。