Mam klasę, która jest 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);
}
}
Kiedy dodam listę obiektów tej klasy do zestawu skrótów:
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);
Wszystko jest w porządku i ha.count
jest 2
, ale:
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));
Teraz ha.count
jest 3
.
- Dlaczego nie
HashSet
szanowaća
„sCompareTo
metody. - Czy
HashSet
najlepiej jest mieć listę unikalnych obiektów?
IEqualityComparer<T>
w konstruktorze lub zaimplementuj ją w klasiea
. msdn.microsoft.com/en-us/library/bb301504(v=vs.110).aspxOdpowiedzi:
Używa
IEqualityComparer<T>
(EqualityComparer<T>.Default
chyba że określisz inny w konstrukcji).Kiedy dodasz element do zestawu, znajdzie on kod skrótu za pomocą
IEqualityComparer<T>.GetHashCode
i zapisze zarówno kod skrótu, jak i element (oczywiście po sprawdzeniu, czy element jest już w zestawie).Aby wyszukać element, najpierw użyje on
IEqualityComparer<T>.GetHashCode
do znalezienia kodu skrótu, a następnie dla wszystkich elementów z tym samym kodem skrótu użyjeIEqualityComparer<T>.Equals
do porównania rzeczywistej równości.Oznacza to, że masz dwie możliwości:
IEqualityComparer<T>
konstruktor. Jest to najlepsza opcja, jeśli nie możesz zmodyfikowaćT
samego siebie lub jeśli chcesz mieć inną niż domyślną relację równości (np. „Wszyscy użytkownicy z ujemnym identyfikatorem użytkownika są traktowani jako równi”). Prawie nigdy nie jest to implementowane w samym typie (tj.Foo
Nie implementujeIEqualityComparer<Foo>
), ale w osobnym typie, który jest używany tylko do porównań.GetHashCode
iEquals(object)
. W idealnym przypadku zaimplementuj równieżIEquatable<T>
w typie, szczególnie jeśli jest to typ wartości. Te metody będą wywoływane przez domyślną funkcję porównującą równość.Zwróć uwagę, że nic z tego nie dotyczy uporządkowanego porównania - co ma sens, ponieważ z pewnością są sytuacje, w których można łatwo określić równość, ale nie całkowitą kolejność. To wszystko jest takie samo jak
Dictionary<TKey, TValue>
w zasadzie.Jeśli chcesz zestaw, który używa porządkowania zamiast tylko porównań równości, powinieneś użyć
SortedSet<T>
z .NET 4 - co pozwala określićIComparer<T>
zamiastIEqualityComparer<T>
. Spowoduje to użycieIComparer<T>.Compare
- które będzie delegować doIComparable<T>.CompareTo
lubIComparable.CompareTo
jeśli używaszComparer<T>.Default
.źródło
IEqualityComparer<T>.GetHashCode/Equals()
jest wdrożenieEquals
iGetHashCode
naT
sobie (a robiąc to, zaimplementowałbyś również silnie wpisany odpowiednik : -bool IEquatable<T>.Equals(T other)
)Equals
iGetHashCode
wystarczy - jak wspomniano w odpowiedzi @ tyriker użytkownika.IComparable
(lub jeśliIComparer
o to chodzi) nie powinieneś być proszony o wprowadzenie równości osobno (ale tylkoGetHashCode
). W pewnym sensie interfejsy porównywalności powinny dziedziczyć po interfejsach równości. Rozumiem korzyści wydajnościowe wynikające z posiadania dwóch oddzielnych funkcji (gdzie można optymalizować równość osobno, mówiąc tylko, czy coś jest równe, czy nie), ale nadal ... Bardzo mylące, jeśli określono, kiedy wystąpienia są równe pod względemCompareTo
funkcji i struktury, których nie będzie rozważać że.a.boolProp == b.boolProp ? 1 : 0
czy powinno byća.boolProp == b.boolProp ? 0 : -1
luba.boolProp == b.boolProp ? 1 : -1
. Fuj!Oto wyjaśnienie części odpowiedzi, która została niewypowiedziana: Typ obiektu
HashSet<T>
nie musi być implementowany,IEqualityComparer<T>
ale zamiast tego musi nadpisaćObject.GetHashCode()
iObject.Equals(Object obj)
.Zamiast tego:
Robisz to:
Jest to subtelne, ale przez większą część dnia denerwowało mnie, próbując sprawić, by HashSet działał tak, jak powinien. I jak powiedzieli inni, w
HashSet<a>
końcu zadzwonia.GetHashCode()
ia.Equals(obj)
w razie potrzeby podczas pracy z zestawem.źródło
bool IEquatable<T>.Equals(T other)
aby uzyskać niewielki wzrost wydajności, ale co ważniejsze, korzyść z przejrzystości. Z powodów obv, oprócz konieczności implementacjiGetHashCode
obokIEquatable<T>
, dokument dla IEquatable <T> wspomina, że dla celów spójności należy również zastąpićobject.Equals
spójnośćovveride getHashcode
działa, aleoverride bool equals
dostaje błąd: nie znaleziono sposób, aby zastąpić. dowolny pomysł?public class a : IEqualityComparer<a> {
, a potemnew HashSet<a>(a)
.HashSet
używaEquals
iGetHashCode()
.CompareTo
dotyczy zestawów zamówionych.Jeśli chcesz unikalnych obiektów, ale nie zależy Ci na ich kolejności iteracji,
HashSet<T>
jest to zazwyczaj najlepszy wybór.źródło
konstruktor HashSet otrzyma obiekt, który implementuje IEqualityComparer do dodawania nowego obiektu. jeśli chcesz użyć metody w HashSet, nie możesz zastąpić Equals, GetHashCode
źródło