У меня есть класс 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
«sCompareTo
метод. - Есть
HashSet
лучший способ получить список уникальных объектов?
IEqualityComparer<T>
в конструктор или реализуйте ее в классеa
. msdn.microsoft.com/en-us/library/bb301504(v=vs.110).aspxОтветы:
Он использует
IEqualityComparer<T>
(EqualityComparer<T>.Default
если вы не укажете другой при строительстве).Когда вы добавляете элемент в набор, он найдет хэш-код с помощью
IEqualityComparer<T>.GetHashCode
и сохранит как хеш-код, так и элемент (конечно, после проверки, есть ли элемент уже в наборе).Чтобы найти элемент, он сначала будет использовать
IEqualityComparer<T>.GetHashCode
для поиска хэш-кода, а затем для всех элементов с одинаковым хеш-кодом он будет использоватьIEqualityComparer<T>.Equals
для сравнения на предмет фактического равенства.Это означает, что у вас есть два варианта:
IEqualityComparer<T>
в конструктор. Это лучший вариант, если вы не можете изменитьT
саму себя или если вы хотите, чтобы отношение равенства отличалось от установленного по умолчанию (например, «все пользователи с отрицательным идентификатором пользователя считаются равными»). Это почти никогда не реализуется в самом типе (т.е.Foo
не реализуетсяIEqualityComparer<Foo>
), а в отдельном типе, который используется только для сравнения.GetHashCode
иEquals(object)
. В идеале также реализоватьIEquatable<T>
в типе, особенно если это тип значения. Эти методы будут вызываться компаратором проверки на равенство по умолчанию.Обратите внимание, что все это не относится к упорядоченному сравнению - что имеет смысл, поскольку, безусловно, есть ситуации, когда вы можете легко указать равенство, но не полное упорядочение. Это все то же самое
Dictionary<TKey, TValue>
, в принципе.Если вам нужен набор, в котором используется упорядочение, а не просто сравнение на равенство, вы должны использовать
SortedSet<T>
из .NET 4, который позволяет вам указыватьIComparer<T>
вместоIEqualityComparer<T>
. Это будет использоватьIComparer<T>.Compare
- который будет делегироватьIComparable<T>.CompareTo
или,IComparable.CompareTo
если вы используетеComparer<T>.Default
.источник
IEqualityComparer<T>.GetHashCode/Equals()
- это реализоватьEquals
иGetHashCode
наT
себе (и пока вы это делаете, вы также должны реализовать строго типизированный аналог : -bool IEquatable<T>.Equals(T other)
)Equals
и этогоGetHashCode
достаточно - как упоминалось в ответе @tyriker.IComparable
(или, еслиIComparer
на то пошло), вас не должны просить реализовать равенство отдельно (а простоGetHashCode
). В некотором смысле интерфейсы сопоставимости должны унаследовать от интерфейсов равенства. Я понимаю преимущества в производительности от наличия двух отдельных функций (где вы можете оптимизировать равенство по отдельности, просто указав, совпадает ли что-то с этим или нет), но все же ... Очень запутанно иначе, когда вы указали, когда экземпляры равны поCompareTo
функциям, а структура не будет учитывать который.a.boolProp == b.boolProp ? 1 : 0
или должно бытьa.boolProp == b.boolProp ? 0 : -1
илиa.boolProp == b.boolProp ? 1 : -1
. Юк!Вот пояснение к части ответа, которая осталась недосказанной: тип вашего объекта
HashSet<T>
не должен реализовываться,IEqualityComparer<T>
а просто должен переопределитьObject.GetHashCode()
иObject.Equals(Object obj)
.Вместо этого:
Ты делаешь это:
Это тонко, но это сбивало меня с толку большую часть дня, пытаясь заставить HashSet работать так, как задумано. И, как говорили другие, в
HashSet<a>
конечном итоге позвонитa.GetHashCode()
иa.Equals(obj)
при необходимости при работе с набором.источник
bool IEquatable<T>.Equals(T other)
для небольшого повышения эффективности, но, что более важно, преимущества ясности. По очевидным причинам, помимо необходимости реализацииGetHashCode
параллельноIEquatable<T>
, в документе для IEquatable <T> упоминается, что для обеспечения согласованности вы также должны переопределитьobject.Equals
для согласованностиovveride getHashcode
работает, ноoverride bool equals
получает ошибку: ни один метод не нашел для переопределения. любая идея?public class a : IEqualityComparer<a> {
, а затемnew HashSet<a>(a)
.HashSet
используетEquals
иGetHashCode()
.CompareTo
для заказанных наборов.Если вам нужны уникальные объекты, но вам не важен порядок их итераций,
HashSet<T>
обычно это лучший выбор.источник
Конструктор HashSet получает объект, который реализует IEqualityComparer для добавления нового объекта. если вы хотите использовать метод в HashSet, вам необходимо переопределить Equals, GetHashCode
источник