2012-06-20 16 views
22
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 los dos elementos al conjunto. Me sorprendió al principio porque al agregar métodos para establecer, se invoca el método igual.HashSet permite la inserción de elementos duplicados si hashCode() no se reemplaza

Pero luego me hizo caso omiso el método hashCode:

@Override 
    public int hashCode() { 
     final int prime = 31; 
     int result = 1; 
     result = prime * result + id; 
     return result; 
    } 

Y entonces no agregó. Esto es sorprendente ya que el método Javadoc de Set y add() dice que solo verifica() mientras se agrega en el Conjunto.

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&nbsp;?&nbsp;e2==null&nbsp;:&nbsp;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; 
    } 

Entonces me di cuenta de que el HashSet se implementa como un HashMap y en el mapa, el código hash del objeto se utiliza como clave. Por lo tanto, les está tratando con diferentes claves si no reemplaza hashCode.

¿No debería estar esto en la documentación del método add() o de HashSet?

+2

El motivo completo por el que existe la función hashCode() es para colecciones basadas en hash. ¿Cómo debe una colección "saber" qué función hash usar si no la define? –

+0

Descubrí que el método equals() de la temperatura de clase nunca se llama en este caso (intentado sysout), por lo que la JVM invoca el valor predeterminado igual() si no tenemos implementado hashCode() para cumplir el contrato de JVM especificación. Interesante !!!!!!! –

+2

Llama solo 'igual' si los' hashCode() 'son los mismos. No llama al valor predeterminado igual si lo reemplaza. –

Respuesta

20

Está algo documentado. Consulte la documentación de java.lang.Object, donde dice en hashCode():

Si dos objetos son iguales de acuerdo a las equals (Object) método, entonces llamando al método hashCode en cada uno de los dos objetos debe producir el mismo resultado entero resultado.

Adicionalmente la siguiente se encuentra en la documentación de la Object.equals(Object) método:

Tenga en cuenta que en general es necesario reemplazar el método hashCode cada vez que se anula este método, a fin de mantener al general contrato para el método hashCode, que establece que objetos iguales deben tener con códigos hash iguales.

En otras palabras, si con su clase cuando instanceA.equals(instanceB) == true y instanceA.hashCode() != istanceB.hashCode() que son, de hecho, que viola el contrato de la clase Object.

+1

Siempre leo que equals() y hashCode() ambos deberían ser anulados, pero nunca pensé que no hacerlo produciría resultados impredecibles. Además, dado que la documentación de los métodos Set y add() especifica que es obligatorio anular el método equals pero no se menciona a hashCode(). El contrato lo abarca todo. –

+0

@DhwaneetBhatt Me alegro de poder ayudarlo. De hecho, cuando se almacenan objetos en los conjuntos de Java, la implementación de ambos métodos es importante. No digo que deba pasarse por alto cuando no hay necesidad aparente o inmediata de hacerlo. Sin embargo, una implementación adecuada (según los contratos) puede ser bastante trivial para soñar. – Kallja

+0

¡Gracias por la respuesta! –

14

Basta con echar un vistazo también en equals() documentación:

Tenga en cuenta que en general es necesario reemplazar el método hashCode cada vez que se anula este método, a fin de mantener el contrato general para el método hashCode, que establece que objetos iguales deben tener códigos hash iguales.

El hecho es que equals() y hashCode() están estrechamente ligadas. Ambos deben considerarse siempre cuando se trabaja con uno de ellos para evitar estos problemas de consistencia.

+0

Lo aprendí de la manera difícil hoy :) –

+0

Jack en cuestión ¿por qué igual(); método tiene Tipo de objeto en el parámetro? aún fundiendo, ¿por qué no dirigir el parámetro de tipo de clase de temperatura? – UnKnown

8

Si anula igual() que debe anular hashCode() también.

Existen algunas restricciones sobre 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 sólo si b.equals (a)
  • reflexividad : Para todas las referencias que no son nulos, 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 hashCode()

Ver this para más detalles.

+0

Gracias por el enlace –

+1

Qué interesante que todo está inspirado en Matemáticas en todas partes;) –

1

Ellos (los chicos javadoc) podrían haber asumido previamente que cuando dicen (en la documentación de add() método en el HashSet)

la hashCode() es inherentemente igual para ambos.

+1

De acuerdo, aunque la presunción es válida ya que uno que usa un conjunto debe estar familiarizado con los métodos relacionados, como Object.equals (Object), que de acuerdo con la documentación del método respectivo está fuertemente relacionado con el método Object.hashCode(). – Kallja

+0

@Jarkko: consulte el anser actualizado –

Cuestiones relacionadas