IComparable
のクラスがあります:
public class a : IComparable
{
public int Id { get; set; }
public string Name { get; set; }
public a(int id)
{
this.Id = id;
}
public int CompareTo(object obj)
{
return this.Id.CompareTo(((a)obj).Id);
}
}
このクラスのオブジェクトのリストをハッシュセットに追加すると:
a a1 = new a(1);
a a2 = new a(2);
HashSet<a> ha = new HashSet<a>();
ha.add(a1);
ha.add(a2);
ha.add(a1);
すべてが正常で、ha.count
は2
ですが、次のとおりです。
a a1 = new a(1);
a a2 = new a(2);
HashSet<a> ha = new HashSet<a>();
ha.add(a1);
ha.add(a2);
ha.add(new a(1));
現在、ha.count
は3
です。
HashSet
がa
のCompareTo
メソッドを尊重しないのはなぜですか。HashSet
は、一意のオブジェクトのリストを作成する最良の方法ですか?IEqualityComparer<T>
を使用します(構築時に別のものを指定しない限り、 EqualityComparer<T>.Default
)。
要素をセットに追加すると、IEqualityComparer<T>.GetHashCode
を使用してハッシュコードを検索し、ハッシュコードと要素の両方を格納します(もちろん、要素が既にセットに含まれているかどうかを確認した後)。
要素を検索するには、最初にIEqualityComparer<T>.GetHashCode
を使用してハッシュコードを検索し、次に同じハッシュコードを持つすべての要素に対してIEqualityComparer<T>.Equals
を使用して実際の同等性を比較します。
つまり、次の2つのオプションがあります。
IEqualityComparer<T>
をコンストラクターに渡します。これは、T
自体を変更できない場合、またはデフォルト以外の等値関係が必要な場合(たとえば、「負のユーザーIDを持つすべてのユーザーが等しいと見なされる」)に最適なオプションです。これは、型自体にはほとんど実装されません(つまり、Foo
はIEqualityComparer<Foo>
を実装しません)が、比較にのみ使用される別の型に実装されます。GetHashCode
とEquals(object)
をオーバーライドして、型自体に平等を実装します。特に値型の場合は、型にもIEquatable<T>
を実装するのが理想的です。これらのメソッドは、デフォルトの等値比較器によって呼び出されます。ordered比較の観点からこれがどれもないことに注意してください-合計ではなく、簡単に等式を指定できる状況は確かにあるため、これは理にかなっています注文。これは基本的にDictionary<TKey, TValue>
と同じです。
単に等価比較の代わりにorderingを使用するセットが必要な場合は、 SortedSet<T>
from .NET 4-を使用する必要がありますIComparer<T>
ではなくIEqualityComparer<T>
を指定します。これはIComparer<T>.Compare
を使用します-IComparable<T>.CompareTo
を使用している場合は、IComparable.CompareTo
またはComparer<T>.Default
に委任します。
HashSet
はEquals
およびGetHashCode()
を使用します。
CompareTo
は順序付きセット用です。
一意のオブジェクトが必要であるが、その繰り返し順序は気にしない場合、通常、HashSet<T>
が最良の選択です。
コンストラクターHashSetは、新しいオブジェクトを追加するためのIEqualityComparerを実装するオブジェクトを受け取ります。 HashSetでメソッドを使用したい場合、Equals、GetHashCodeをオーバーライドします。
namespace HashSet
{
public class Employe
{
public Employe() {
}
public string Name { get; set; }
public override string ToString() {
return Name;
}
public override bool Equals(object obj) {
return this.Name.Equals(((Employe)obj).Name);
}
public override int GetHashCode() {
return this.Name.GetHashCode();
}
}
class EmployeComparer : IEqualityComparer<Employe>
{
public bool Equals(Employe x, Employe y)
{
return x.Name.Trim().ToLower().Equals(y.Name.Trim().ToLower());
}
public int GetHashCode(Employe obj)
{
return obj.Name.GetHashCode();
}
}
class Program
{
static void Main(string[] args)
{
HashSet<Employe> hashSet = new HashSet<Employe>(new EmployeComparer());
hashSet.Add(new Employe() { Name = "Nik" });
hashSet.Add(new Employe() { Name = "Rob" });
hashSet.Add(new Employe() { Name = "Joe" });
Display(hashSet);
hashSet.Add(new Employe() { Name = "Rob" });
Display(hashSet);
HashSet<Employe> hashSetB = new HashSet<Employe>(new EmployeComparer());
hashSetB.Add(new Employe() { Name = "Max" });
hashSetB.Add(new Employe() { Name = "Solomon" });
hashSetB.Add(new Employe() { Name = "Werter" });
hashSetB.Add(new Employe() { Name = "Rob" });
Display(hashSetB);
var union = hashSet.Union<Employe>(hashSetB).ToList();
Display(union);
var inter = hashSet.Intersect<Employe>(hashSetB).ToList();
Display(inter);
var except = hashSet.Except<Employe>(hashSetB).ToList();
Display(except);
Console.ReadKey();
}
static void Display(HashSet<Employe> hashSet)
{
if (hashSet.Count == 0)
{
Console.Write("Collection is Empty");
return;
}
foreach (var item in hashSet)
{
Console.Write("{0}, ", item);
}
Console.Write("\n");
}
static void Display(List<Employe> list)
{
if (list.Count == 0)
{
Console.WriteLine("Collection is Empty");
return;
}
foreach (var item in list)
{
Console.Write("{0}, ", item);
}
Console.Write("\n");
}
}
}