java - redefinir - HashSet permite la inserción de elementos duplicados si hashCode() no se anula
override hashcode java (4)
class temp {
int id;
public int getId() {
return id;
}
temp(int id) {
this.id = id;
}
public void setId(int id) {
this.id = id;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
temp other = (temp) obj;
if (id != other.id)
return false;
return true;
}
}
public class testClass {
public static void main(String[] args) {
temp t1 = new temp(1);
temp t2 = new temp(1);
System.out.println(t1.equals(t2));
Set<temp> tempList = new HashSet<temp>(2);
tempList.add(t1);
tempList.add(t2);
System.out.println(tempList);
}
El programa agrega ambos elementos al Conjunto. Al principio me sorprendí porque al agregar métodos para establecer, se invoca el método igual.
Pero luego anulé el método hashCode:
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
Y luego no se sumó. Esto es sorprendente, ya que el método Javadoc de Set y add () dice que solo verifica los iguales () al agregar al Set.
Y este es el javadoc para add ():
/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null ? e2==null : e.equals(e2))</tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
Luego me di cuenta de que el HashSet se implementa como un HashMap y en el mapa, el hashCode del objeto se usa como la clave. Por lo tanto, los está tratando con claves diferentes si no anula el código hash.
¿No debería estar esto en la documentación del método add () o en el de HashSet?
Eche un vistazo también a la documentación de equals()
:
Tenga en cuenta que generalmente es necesario anular el método hashCode siempre que este método se anule, a fin de mantener el contrato general para el método hashCode, que establece que los objetos iguales deben tener códigos hash iguales .
El hecho es que equals()
y hashCode()
están fuertemente vinculados. Ambos deben considerarse siempre cuando se trabaja con uno de ellos para evitar estos problemas de consistencia.
Ellos (los chicos de javadoc) podrían haber asumido previamente que cuando dicen (en la documentación del método add()
en HashSet
)
(e==null ? e2==null : e.equals(e2))
hashCode()
es inherentemente igual para ambos.
Está algo documentado. Consulte la documentación para java.lang.Object , donde dice en hashCode()
:
Si dos objetos son iguales según el método equals (Objeto) , entonces llamar al método hashCode en cada uno de los dos objetos debe producir el mismo resultado entero .
Además, se encuentra lo siguiente en la documentación del Object.equals(Object)
:
Tenga en cuenta que generalmente es necesario anular el método hashCode siempre que este método se anule, a fin de mantener el contrato general para el método hashCode, que establece que los objetos iguales deben tener códigos hash iguales .
En otras palabras, si está con su clase cuando instanceA.equals(instanceB) == true
y instanceA.hashCode() != istanceB.hashCode()
de hecho está violando el contrato de la clase Object.
Si reemplaza a equals () también debe invalidar hashCode ().
Existen algunas restricciones en el comportamiento de equals () y hashCode (), que se enumeran en la documentación de Object. En particular, el método equals () debe exhibir las siguientes propiedades:
- Simetría: para dos referencias, a y b, a.equals (b) si y solo si b.equals (a)
- Reflexividad: para todas las referencias que no sean nulas, a.equals (a)
- Transitividad: si a.equals (b) y b.equals (c), entonces a.equals (c)
- Coherencia con hashCode (): dos objetos iguales deben tener el mismo valor de hashCode ()
Vea this para más detalles.