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);
}
}
Quand 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 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));
À présent ha.count
est 3
.
HashSet
ne respecte-t-il pas la méthode a
de CompareTo
?.HashSet
est le meilleur moyen d'avoir une liste d'objets uniques?Il utilise un IEqualityComparer<T>
( EqualityComparer<T>.Default
sauf si vous en spécifiez un autre lors de la construction).
Lorsque vous ajoutez un élément à l'ensemble, il trouve le code de hachage à l'aide de IEqualityComparer<T>.GetHashCode
Et stocke à 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 ayant le même code de hachage, il utilisera IEqualityComparer<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 le T
lui-même, ou si vous souhaitez 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.-à-d. Foo
n’implémente pas IEqualityComparer<Foo>
) Mais dans un type séparé qui n’est utilisé que pour les comparaisons.GetHashCode
et Equals(object)
. Idéalement, implémentez IEquatable<T>
Dans le type également, en particulier s'il s'agit d'un type de 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 existe certainement des situations où vous pouvez facilement spécifier une égalité mais pas un total. commande. Ceci est identique à Dictionary<TKey, TValue>
, En gros.
Si vous voulez un ensemble qui utilise la commande au lieu de simples comparaisons d’égalité, vous devez utiliser SortedSet<T>
de. NET 4 - qui vous permet de spécifier un IComparer<T>
Au lieu de IEqualityComparer<T>
. Ceci utilisera IComparer<T>.Compare
- qui déléguera à IComparable<T>.CompareTo
Ou IComparable.CompareTo
Si vous utilisez Comparer<T>.Default
.
Voici des éclaircissements sur une partie de la réponse qui n'a pas été dite: le type d'objet de votre HashSet<T>
Ne doit pas nécessairement implémenter IEqualityComparer<T>
, Mais simplement écraser Object.GetHashCode()
et Object.Equals(Object obj)
.
Au lieu de cela:
public class a : IEqualityComparer<a>
{
public int GetHashCode(a obj) { /* Implementation */ }
public bool Equals(a obj1, a obj2) { /* Implementation */ }
}
Tu fais cela:
public class a
{
public override int GetHashCode() { /* Implementation */ }
public override bool Equals(object obj) { /* Implementation */ }
}
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 appeler a.GetHashCode()
et a.Equals(obj)
selon les besoins lors de l’utilisation de l’ensemble.
HashSet
utilise Equals
et GetHashCode()
.
CompareTo
est pour les ensembles ordonnés.
Si vous voulez des objets uniques, mais vous ne vous souciez pas de leur ordre d'itération, HashSet<T>
est généralement le meilleur choix.
le constructeur HashSet reçoit un objet qui implémente IEqualityComparer pour ajouter un nouvel objet. si vous voulez utiliser une méthode dans HashSet, vous ne devez pas remplacer Equals, GetHashCode
namespace HashSet
{
public class Employe
{
public Employe() {
}
public string Name { get; set; }
public override string ToString() {
return Name;
}
public override bool Equals(object obj) {
return this.Name.Equals(((Employe)obj).Name);
}
public override int GetHashCode() {
return this.Name.GetHashCode();
}
}
class EmployeComparer : IEqualityComparer<Employe>
{
public bool Equals(Employe x, Employe y)
{
return x.Name.Trim().ToLower().Equals(y.Name.Trim().ToLower());
}
public int GetHashCode(Employe obj)
{
return obj.Name.GetHashCode();
}
}
class Program
{
static void Main(string[] args)
{
HashSet<Employe> hashSet = new HashSet<Employe>(new EmployeComparer());
hashSet.Add(new Employe() { Name = "Nik" });
hashSet.Add(new Employe() { Name = "Rob" });
hashSet.Add(new Employe() { Name = "Joe" });
Display(hashSet);
hashSet.Add(new Employe() { Name = "Rob" });
Display(hashSet);
HashSet<Employe> hashSetB = new HashSet<Employe>(new EmployeComparer());
hashSetB.Add(new Employe() { Name = "Max" });
hashSetB.Add(new Employe() { Name = "Solomon" });
hashSetB.Add(new Employe() { Name = "Werter" });
hashSetB.Add(new Employe() { Name = "Rob" });
Display(hashSetB);
var union = hashSet.Union<Employe>(hashSetB).ToList();
Display(union);
var inter = hashSet.Intersect<Employe>(hashSetB).ToList();
Display(inter);
var except = hashSet.Except<Employe>(hashSetB).ToList();
Display(except);
Console.ReadKey();
}
static void Display(HashSet<Employe> hashSet)
{
if (hashSet.Count == 0)
{
Console.Write("Collection is Empty");
return;
}
foreach (var item in hashSet)
{
Console.Write("{0}, ", item);
}
Console.Write("\n");
}
static void Display(List<Employe> list)
{
if (list.Count == 0)
{
Console.WriteLine("Collection is Empty");
return;
}
foreach (var item in list)
{
Console.Write("{0}, ", item);
}
Console.Write("\n");
}
}
}