Ich habe eine Klasse, die ist 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);
}
}
Wenn ich eine Liste von Objekten dieser Klasse zu einem Hash-Set hinzufüge:
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);
Alles ist gut und ha.count
ist 2
, aber:
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));
Jetzt ha.count
ist 3
.
- Warum
HashSet
respektiert man nichta
dieCompareTo
Methode? - Ist
HashSet
der beste Weg, um eine Liste einzigartiger Objekte zu haben?
IEqualityComparer<T>
im Konstruktor hinzu oder implementieren Sie sie in der Klassea
. msdn.microsoft.com/en-us/library/bb301504(v=vs.110).aspxAntworten:
Es wird ein verwendet
IEqualityComparer<T>
(esEqualityComparer<T>.Default
sei denn, Sie geben bei der Konstruktion ein anderes an).Wenn Sie der Menge ein Element hinzufügen, findet es den Hash-Code mithilfe von
IEqualityComparer<T>.GetHashCode
und speichert sowohl den Hash-Code als auch das Element (nachdem Sie natürlich überprüft haben, ob sich das Element bereits in der Menge befindet).Um ein Element nachzuschlagen, wird zuerst der verwendet
IEqualityComparer<T>.GetHashCode
, um den Hash-Code zu finden, und dann wird für alle Elemente mit demselben Hash-CodeIEqualityComparer<T>.Equals
der Vergleich für die tatsächliche Gleichheit verwendet.Das heißt, Sie haben zwei Möglichkeiten:
IEqualityComparer<T>
an den Konstruktor. Dies ist die beste Option, wenn Sie dasT
selbst nicht ändern können oder wenn Sie eine nicht standardmäßige Gleichheitsrelation wünschen (z. B. "Alle Benutzer mit einer negativen Benutzer-ID werden als gleich angesehen"). Dies wird fast nie auf dem Typ selbst implementiert (dhFoo
nicht implementiertIEqualityComparer<Foo>
), sondern in einem separaten Typ, der nur für Vergleiche verwendet wird.GetHashCode
und überschreibenEquals(object)
. Im Idealfall auchIEquatable<T>
im Typ implementieren , insbesondere wenn es sich um einen Werttyp handelt. Diese Methoden werden vom Standardgleichheitsvergleich aufgerufen.Beachten Sie, dass dies im Hinblick auf einen geordneten Vergleich nichts ist - was sinnvoll ist, da es sicherlich Situationen gibt, in denen Sie leicht die Gleichheit angeben können, aber keine Gesamtreihenfolge. Dies ist im
Dictionary<TKey, TValue>
Grunde alles das Gleiche wie .Wenn Sie eine Menge möchten, die die Reihenfolge anstelle von Gleichheitsvergleichen verwendet, sollten Sie sie
SortedSet<T>
aus .NET 4 verwenden, mit der Sie eineIComparer<T>
anstelle einer angeben könnenIEqualityComparer<T>
. Dies wird verwendetIComparer<T>.Compare
- was anIComparable<T>.CompareTo
oderIComparable.CompareTo
wenn Sie verwenden delegierenComparer<T>.Default
.quelle
IEqualityComparer<T>.GetHashCode/Equals()
zu implementierenEquals
undGetHashCode
aufT
sich selbst (und während Sie das tun, dann würden Sie auch die stark typisierte Gegenstück implementieren : -bool IEquatable<T>.Equals(T other)
)Equals
undGetHashCode
ausreicht - wie in der Antwort von @ tyriker erwähnt.IComparable
(oder imIComparer
Übrigen) nicht aufgefordert werden, die Gleichstellung separat (sondern nurGetHashCode
) zu implementieren . In gewissem Sinne sollten die Vergleichbarkeitsschnittstellen von Gleichheitsschnittstellen erben. Ich verstehe die Leistungsvorteile, wenn zwei separate Funktionen vorhanden sind (bei denen Sie die Gleichheit separat optimieren können, indem Sie einfach sagen, ob etwas gleich ist oder nicht), aber dennoch. Ansonsten sehr verwirrend, wenn Sie angegeben haben, wann die Instanzen inCompareTo
Funktion und Framework gleich sind, werden dies nicht berücksichtigt Das.a.boolProp == b.boolProp ? 1 : 0
oder sollte es seina.boolProp == b.boolProp ? 0 : -1
odera.boolProp == b.boolProp ? 1 : -1
. Yuk!Hier ist eine Klarstellung zu einem Teil der Antwort, der nicht gesagt wurde: Der Objekttyp von Ihnen
HashSet<T>
muss nicht implementiert werden,IEqualityComparer<T>
sondern muss nur überschreibenObject.GetHashCode()
undObject.Equals(Object obj)
.An Stelle von:
Du machst das:
Es ist subtil, aber das hat mich fast einen ganzen Tag lang gestolpert, als ich versucht habe, HashSet so zu machen, wie es beabsichtigt ist. Und wie andere gesagt haben,
HashSet<a>
wird am Ende anrufena.GetHashCode()
unda.Equals(obj)
nach Bedarf bei der Arbeit mit dem Set.quelle
bool IEquatable<T>.Equals(T other)
einen leichten Effizienzgewinn, aber vor allem den Vorteil der Klarheit implementieren . Für Vs. Gründen neben der Notwendigkeit zur UmsetzungGetHashCode
nebenIEquatable<T>
der doc für IEquatable <T> erwähnt , dass für Konsistenz Zwecke sollten Sie auch die außer Kraft setzenobject.Equals
auf Konsistenzovveride getHashcode
funktioniert,override bool equals
bekommt aber den Fehler: Es wurde keine Methode zum Überschreiben gefunden. irgendeine Idee?public class a : IEqualityComparer<a> {
und dann habennew HashSet<a>(a)
.HashSet
verwendetEquals
undGetHashCode()
.CompareTo
ist für bestellte Sets.Wenn Sie eindeutige Objekte möchten, deren Iterationsreihenfolge jedoch nicht wichtig ist,
HashSet<T>
ist dies normalerweise die beste Wahl.quelle
Konstruktor HashSet Empfangsobjekt, das IEqualityComparer zum Hinzufügen eines neuen Objekts implementiert. Wenn Sie die Methode in HashSet verwenden möchten, überschreiben Sie Equals, GetHashCode
quelle