I have a class that is 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);
}
}
When I add a list of object of this class to a hash set:
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);
Everything is fine and ha.count is 2, but:
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));
Now ha.count is 3.
- Why doesn’t
HashSetrespecta‘sCompareTomethod. - Is
HashSetthe best way to have a list of unique objects?
It uses an
IEqualityComparer<T>(EqualityComparer<T>.Defaultunless you specify a different one on construction).When you add an element to the set, it will find the hash code using
IEqualityComparer<T>.GetHashCode, and store both the hash code and the element (after checking whether the element is already in the set, of course).To look an element up, it will first use the
IEqualityComparer<T>.GetHashCodeto find the hash code, then for all elements with the same hash code, it will useIEqualityComparer<T>.Equalsto compare for actual equality.That means you have two options:
IEqualityComparer<T>into the constructor. This is the best option if you can’t modify theTitself, or if you want a non-default equality relation (e.g. “all users with a negative user ID are considered equal”). This is almost never implemented on the type itself (i.e.Foodoesn’t implementIEqualityComparer<Foo>) but in a separate type which is only used for comparisons.GetHashCodeandEquals(object). Ideally, implementIEquatable<T>in the type as well, particularly if it’s a value type. These methods will be called by the default equality comparer.Note how none of this is in terms of an ordered comparison – which makes sense, as there are certainly situations where you can easily specify equality but not a total ordering. This is all the same as
Dictionary<TKey, TValue>, basically.If you want a set which uses ordering instead of just equality comparisons, you should use
SortedSet<T>from .NET 4 – which allows you to specify anIComparer<T>instead of anIEqualityComparer<T>. This will useIComparer<T>.Compare– which will delegate toIComparable<T>.CompareToorIComparable.CompareToif you’re usingComparer<T>.Default.