J'ai une classe qui est 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);
}
}
Lorsque j'ajoute une liste d'objets de cette classe à un ensemble de hachage:
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);
Tout va bien et l' ha.count
est 2
, mais:
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));
Maintenant ha.count
est 3
.
- Pourquoi ne
HashSet
respecte pasa
laCompareTo
méthode de. - Est-ce
HashSet
la meilleure façon d'avoir une liste d'objets uniques?
IEqualityComparer<T>
dans le constructeur ou implémentez-la dans la classea
. msdn.microsoft.com/en-us/library/bb301504(v=vs.110).aspxRéponses:
Il utilise un
IEqualityComparer<T>
(EqualityComparer<T>.Default
sauf si vous en spécifiez un différent lors de la construction).Lorsque vous ajoutez un élément à l'ensemble, il trouvera le code de hachage à l'aide de
IEqualityComparer<T>.GetHashCode
, et stockera à la fois le code de hachage et l'élément (après avoir vérifié si l'élément est déjà dans l'ensemble, bien sûr).Pour rechercher un élément, il utilisera d'abord le
IEqualityComparer<T>.GetHashCode
pour trouver le code de hachage, puis pour tous les éléments avec le même code de hachage, il utiliseraIEqualityComparer<T>.Equals
pour comparer l'égalité réelle.Cela signifie que vous avez deux options:
IEqualityComparer<T>
dans le constructeur. C'est la meilleure option si vous ne pouvez pas modifier leT
lui - même, ou si vous voulez une relation d'égalité autre que celle par défaut (par exemple, "tous les utilisateurs avec un ID utilisateur négatif sont considérés comme égaux"). Ceci n'est presque jamais implémenté sur le type lui-même (c'est-àFoo
- dire ne l'implémente pasIEqualityComparer<Foo>
) mais dans un type séparé qui n'est utilisé que pour les comparaisons.GetHashCode
etEquals(object)
. Idéalement, implémentez égalementIEquatable<T>
le type, en particulier s'il s'agit d'un type valeur. Ces méthodes seront appelées par le comparateur d'égalité par défaut.Notez que rien de tout cela n'est en termes de comparaison ordonnée - ce qui a du sens, car il y a certainement des situations où vous pouvez facilement spécifier l'égalité mais pas un ordre total. C'est tout de même
Dictionary<TKey, TValue>
fondamentalement.Si vous voulez un ensemble qui utilise l' ordre au lieu de simples comparaisons d'égalité, vous devez utiliser à
SortedSet<T>
partir de .NET 4 - qui vous permet de spécifier unIComparer<T>
au lieu d'unIEqualityComparer<T>
. Cela utiliseraIComparer<T>.Compare
- qui déléguera àIComparable<T>.CompareTo
ouIComparable.CompareTo
si vous utilisezComparer<T>.Default
.la source
IEqualityComparer<T>.GetHashCode/Equals()
est de mettre en œuvreEquals
etGetHashCode
surT
lui-même (et pendant que vous le faites, vous implémenteriez également la contrepartie fortement typée : -bool IEquatable<T>.Equals(T other)
)Equals
etGetHashCode
suffit - comme mentionné dans la réponse de @ tyriker.IComparable
(ouIComparer
d'ailleurs), vous ne devriez pas être invité à implémenter l'égalité séparément (mais justeGetHashCode
). En un sens, les interfaces de comparabilité devraient hériter des interfaces d'égalité. Je comprends les avantages en termes de performances d'avoir deux fonctions distinctes (où vous pouvez optimiser l'égalité séparément simplement en disant si quelque chose est égal ou non) mais quand même .. Très déroutant sinon lorsque vous avez spécifié quand les instances sont égales enCompareTo
fonction et le cadre ne considérera pas cette.a.boolProp == b.boolProp ? 1 : 0
ou devrait-il êtrea.boolProp == b.boolProp ? 0 : -1
oua.boolProp == b.boolProp ? 1 : -1
. Yuk!Voici une clarification sur une partie de la réponse qui n'a pas été dite: le type d'objet de votre
HashSet<T>
n'a pas à être implémentéIEqualityComparer<T>
mais doit simplement remplacerObject.GetHashCode()
etObject.Equals(Object obj)
.Au lieu de cela:
Tu fais cela:
C'est subtil, mais cela m'a fait trébucher pendant la majeure partie de la journée en essayant de faire fonctionner HashSet comme prévu. Et comme d'autres l'ont dit,
HashSet<a>
finira par appelera.GetHashCode()
eta.Equals(obj)
si nécessaire lorsque vous travaillez avec l'ensemble.la source
bool IEquatable<T>.Equals(T other)
pour un léger gain d'efficacité mais plus important encore, l'avantage de la clarté. Pour des raisons évidentes, en plus de la nécessité d'implémenterGetHashCode
parallèlementIEquatable<T>
, le document pour IEquatable <T> mentionne que pour des raisons de cohérence, vous devez également remplacer leobject.Equals
pour la cohérenceovveride getHashcode
fonctionne, maisoverride bool equals
obtient l'erreur: aucune méthode trouvée pour remplacer. une idée?public class a : IEqualityComparer<a> {
, et ensuitenew HashSet<a>(a)
.HashSet
utiliseEquals
etGetHashCode()
.CompareTo
est pour les ensembles commandés.Si vous voulez des objets uniques, mais que vous ne vous souciez pas de leur ordre d'itération,
HashSet<T>
c'est généralement le meilleur choix.la source
Le constructeur HashSet reçoit un objet qui implémente IEqualityComparer pour l'ajout d'un nouvel objet. si vous voulez utiliser la méthode dans HashSet, vous ne remplacez pas Equals, GetHashCode
la source