Tengo una clase que es 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);
}
}
Cuando agrego una lista de objetos de esta clase a un conjunto de hash:
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);
Todo está bien y ha.count
está 2
, pero:
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));
Ahora ha.count
es 3
.
- ¿Por qué no
HashSet
respetaa
elCompareTo
método? - ¿Es
HashSet
la mejor manera de tener una lista de objetos únicos?
IEqualityComparer<T>
en el constructor o impleméntelo en la clasea
. msdn.microsoft.com/en-us/library/bb301504(v=vs.110).aspxRespuestas:
Utiliza un
IEqualityComparer<T>
(aEqualityComparer<T>.Default
menos que especifique uno diferente en la construcción).Cuando agrega un elemento al conjunto, encontrará el código hash usando
IEqualityComparer<T>.GetHashCode
y almacenará tanto el código hash como el elemento (después de verificar si el elemento ya está en el conjunto, por supuesto).Para buscar un elemento, primero usará
IEqualityComparer<T>.GetHashCode
para encontrar el código hash, luego, para todos los elementos con el mismo código hash, usaráIEqualityComparer<T>.Equals
para comparar la igualdad real.Eso significa que tienes dos opciones:
IEqualityComparer<T>
al constructor. Esta es la mejor opción si no puede modificarse aT
sí mismo o si desea una relación de igualdad no predeterminada (por ejemplo, "todos los usuarios con una ID de usuario negativa se consideran iguales"). Esto casi nunca se implementa en el tipo en sí (es decir,Foo
no se implementaIEqualityComparer<Foo>
), sino en un tipo separado que solo se usa para comparaciones.GetHashCode
yEquals(object)
. Idealmente, implemente tambiénIEquatable<T>
en el tipo, particularmente si es un tipo de valor. El método de comparación de igualdad predeterminado llamará a estos métodos.Tenga en cuenta que nada de esto es en términos de una comparación ordenada , lo que tiene sentido, ya que ciertamente hay situaciones en las que puede especificar fácilmente la igualdad, pero no una ordenación total. Todo esto es lo mismo que
Dictionary<TKey, TValue>
, básicamente.Si desea un conjunto que use el ordenamiento en lugar de solo las comparaciones de igualdad, debe usarlo
SortedSet<T>
desde .NET 4, que le permite especificar un enIComparer<T>
lugar de unIEqualityComparer<T>
. Esto usaráIComparer<T>.Compare
, lo que delegaráIComparable<T>.CompareTo
oIComparable.CompareTo
si está usandoComparer<T>.Default
.fuente
IEqualityComparer<T>.GetHashCode/Equals()
es implementarEquals
yGetHashCode
sobreT
sí mismo (y mientras lo hace, también implementaría la contraparte fuertemente tipada : -bool IEquatable<T>.Equals(T other)
)Equals
yGetHashCode
sea suficiente, como se menciona en la respuesta de @ tyriker.IComparable
(oIComparer
para el caso) no se le debe pedir que implemente la igualdad por separado (sino soloGetHashCode
). En cierto sentido, las interfaces de comparabilidad deben heredar de las interfaces de igualdad. Entiendo los beneficios de rendimiento al tener dos funciones separadas (donde puede optimizar la igualdad por separado simplemente diciendo si algo es igual o no) pero aún así. Muy confuso de lo contrario cuando ha especificado cuándo las instancias son iguales enCompareTo
función y marco no considerará ese.a.boolProp == b.boolProp ? 1 : 0
o debería sera.boolProp == b.boolProp ? 0 : -1
oa.boolProp == b.boolProp ? 1 : -1
. Yuk!Aquí hay una aclaración sobre una parte de la respuesta que no se ha dicho: el tipo de objeto de tu
HashSet<T>
no tiene que implementarse,IEqualityComparer<T>
sino que solo debe anularseObject.GetHashCode()
yObject.Equals(Object obj)
.En lugar de esto:
Tu hiciste esto:
Es sutil, pero esto me hizo tropezar la mayor parte del día tratando de hacer que HashSet funcione de la manera prevista. Y como han dicho otros,
HashSet<a>
terminará llamandoa.GetHashCode()
ya.Equals(obj)
según sea necesario cuando trabaje con el conjunto.fuente
bool IEquatable<T>.Equals(T other)
para un ligero aumento de la eficiencia, pero lo más importante es el beneficio de la claridad. Por razones obvias, además de la necesidad de implementarGetHashCode
juntoIEquatable<T>
, el documento de IEquatable <T> menciona que, para fines de coherencia, también debe anular elobject.Equals
de coherenciaovveride getHashcode
obras, perooverride bool equals
obtiene el error: ningún método encontraron a anulación. ¿alguna idea?public class a : IEqualityComparer<a> {
, y luegonew HashSet<a>(a)
.HashSet
usosEquals
yGetHashCode()
.CompareTo
es para conjuntos ordenados.Si desea objetos únicos, pero no le importa su orden de iteración,
HashSet<T>
suele ser la mejor opción.fuente
El constructor HashSet recibe el objeto que implementa IEqualityComparer para agregar un nuevo objeto. si desea utilizar el método en HashSet, debe anular Equals, GetHashCode
fuente