Jak HashSet porównuje elementy dla równości?
Mam klasę 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 zbioru hashó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
HashSet
nie respektujea
metodyCompareTo
. - Czy
HashSet
jest najlepszym sposobem na posiadanie listy unikalnych obiektów?
4 answers
Używa IEqualityComparer<T>
(EqualityComparer<T>.Default
chyba, że podasz inny na budowie).
Gdy 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 IEqualityComparer<T>.GetHashCode
, aby znaleźć kod skrótu, a następnie dla wszystkich elementów z tym samym kodem skrótu użyje IEqualityComparer<T>.Equals
, aby porównać rzeczywiste równość.
Oznacza to, że masz dwie opcje:
- przekazuje własne
IEqualityComparer<T>
do konstruktora. Jest to najlepsza opcja, jeśli nie możesz zmodyfikować samejT
lub jeśli chcesz mieć domyślną relację równości (np. "wszyscy użytkownicy z ujemnym ID użytkownika są traktowani jako równi"). To prawie nigdy nie jest zaimplementowane na samym typie (tzn.Foo
nie implementujeIEqualityComparer<Foo>
), ale w oddzielnym typie, który jest używany tylko do porównań. - zaimplementuj równość w samym typie, przez nadrzędne
GetHashCode
iEquals(object)
. Najlepiej zaimplementowaćIEquatable<T>
również w typie, szczególnie jeśli jest to typ wartości. Metody te będą wywoływane przez domyślny porównywacz równości.
Zauważ, że nic z tego nie jest w kategoriach uporządkowanego porównania - co ma sens, ponieważ są sytuacje, w których można łatwo określić równość, ale nie całkowite uporządkowanie. To wszystko jest takie samo jak Dictionary<TKey, TValue>
, W zasadzie.
Jeśli chcesz zamiast tego zestaw, który używa ordering tylko porównań równości, należy użyć SortedSet<T>
Z. NET 4-który pozwala na podanie IComparer<T>
zamiast IEqualityComparer<T>
. To użyje IComparer<T>.Compare
- które deleguje do IComparable<T>.CompareTo
lub IComparable.CompareTo
, Jeśli używasz Comparer<T>.Default
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-02-01 07:10:10
Oto Wyjaśnienie Części odpowiedzi, która została niewypowiedziana: typ obiektu twojego HashSet<T>
nie musi implementować IEqualityComparer<T>
, ale zamiast tego musi zastąpić Object.GetHashCode()
i Object.Equals(Object obj)
.
Zamiast tego:
public class a : IEqualityComparer<a>
{
public int GetHashCode(a obj) { /* Implementation */ }
public bool Equals(a obj1, a obj2) { /* Implementation */ }
}
Robisz to:
public class a
{
public override int GetHashCode() { /* Implementation */ }
public override bool Equals(object obj) { /* Implementation */ }
}
To jest subtelne, ale to poruszyło mnie przez większą część dnia próbując sprawić, by HashSet działał tak, jak jest zamierzony. I tak jak inni mówili, HashSet<a>
skończy się na wywołaniu a.GetHashCode()
i a.Equals(obj)
w razie potrzeby podczas pracy z gotowi.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-12-12 08:54:56
HashSet
używa Equals
i GetHashCode()
.
CompareTo
jest dla zamówionych zestawów.
Jeśli chcesz unikalnych obiektów, ale nie dbasz o ich kolejność iteracji, HashSet<T>
jest zazwyczaj najlepszym wyborem.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-01-21 09:40:50
Konstruktor HashSet odbiera obiekt, który implementuje Ieequalitycomparer do dodania nowego obiektu. jeśli chcesz użyć metody w HashSet, możesz użyć overrride 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");
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-08-25 12:44:14