2010-04-25 14 views
26

Ok, he escuchado de muchos lugares y fuentes que siempre que sobreescribo el método equals(), también debo reemplazar el método hashCode(). Pero considere el siguiente fragmento de código¿Por qué debería anular hashCode() cuando anulo el método equals()?

package test; 

public class MyCustomObject { 

    int intVal1; 
    int intVal2; 

    public MyCustomObject(int val1, int val2){ 
     intVal1 = val1; 
     intVal2 = val2; 
    } 

    public boolean equals(Object obj){ 
     return (((MyCustomObject)obj).intVal1 == this.intVal1) && 
       (((MyCustomObject)obj).intVal2 == this.intVal2); 
    } 

    public static void main(String a[]){ 
     MyCustomObject m1 = new MyCustomObject(3,5); 
     MyCustomObject m2 = new MyCustomObject(3,5); 
     MyCustomObject m3 = new MyCustomObject(4,5); 

     System.out.println(m1.equals(m2)); 
     System.out.println(m1.equals(m3)); 
    } 
} 

Aquí la salida es verdadero, falso exactamente como yo quiero que sea y no me importa de reemplazando el método hashCode() en absoluto. Esto significa que la anulación de hashCode() es una opción más bien una obligatoria, como dicen todos.

Quiero una segunda confirmación.

+0

posible duplicado de http://stackoverflow.com/questions/371328/why-is-it-important-to-override-gethashcode-when-equals-method-is-overriden-in-c – Oded

+4

No es una tontería de ese; eso es C#, esto es Java. (El problema es muy similar, tal vez idéntico, pero aún así) – Thomas

Respuesta

32

Funciona para usted porque su código no utiliza ninguna funcionalidad (HashMap, HashTable) que necesita el hashCode() API.

Sin embargo, no sabe si su clase (presumiblemente no escrita como una excepción) será llamada más tarde en un código que sí utiliza sus objetos como clave hash, en cuyo caso las cosas se verán afectadas.

Según el documentation for Object class:

El contrato general de hashCode es:

  • Cada vez que se invoca en el mismo objeto más de una vez durante una ejecución de una aplicación Java, el código hash el método debe devolver consistentemente el mismo entero, siempre que no se modifique la información utilizada en comparaciones iguales en el objeto. Este entero no necesita ser consistente desde una ejecución de una aplicación hasta otra ejecución de la misma aplicación.

  • Si dos objetos son iguales según el método equals (Object), al llamar al método hashCode en cada uno de los dos objetos debe producir el mismo resultado entero.

+1

Y, opcionalmente, 'hashCode' debe hacer todo lo posible para no devolver el mismo entero en muchos valores que es probable que ocurran juntos durante la misma ejecución del programa. Una vez tuve un error de rendimiento que me llevó mucho tiempo rastrear hasta usar la igualdad física junto con la función hash predeterminada (estructural). Este uso satisface las condiciones anteriores, pero todos los valores estructuralmente idénticos y físicamente diferentes se redujeron a la misma categoría. –

+0

@Pascal - eso es correcto. De hecho, el documento continúa: * No es necesario que si dos objetos son desiguales de acuerdo con el método igual (java.lang.Object), al llamar al método hashCode en cada uno de los dos objetos debe producir resultados enteros distintos. ** Sin embargo, el programador debe tener en cuenta que la producción de resultados enteros distintos para objetos desiguales puede mejorar el rendimiento de hashtables **. – DVK

+0

"... Tanto como sea razonablemente práctico, el método hashCode definido por la clase Object devuelve enteros distintos para distintos objetos. (Esto se implementa típicamente al convertir la dirección interna del objeto en un entero, pero esta técnica de implementación no es requerido por el lenguaje de programación Java.) " – DVK

10

Porque HashMap/Hashtable buscará el objeto por hashCode() primero.

Si no son lo mismo, hashmap afirmará que el objeto no es el mismo y el retorno no existe en el mapa.

5

La razón por la que necesita @Override ninguno o ambos, se debe a la forma en que se relacionan con el resto de la API.

Encontrarás que si pones m1 en HashSet<MyCustomObject>, entonces no contains(m2). Esto es comportamiento inconsistente y puede causar muchos errores y caos.

La biblioteca de Java tiene toneladas de funcionalidades. Para que funcionen para, debe cumplir las reglas y asegurarse de que equals y hashCode sean consistentes es uno de los más importantes.

4

La mayoría de los otros comentarios ya que dio la respuesta: hay que hacerlo porque hay colecciones (es decir: hashset, HashMap) que utiliza código hash como una optimización en "Índice" instancias de objeto, una esas optimizaciones esperan que si: a.equals(b) ==>a.hashCode() == b.hashCode() (TENGA en cuenta que el inverso no se mantiene).

Pero como la información adicional que puede hacer este ejercicio:

class Box { 
    private String value; 
    /* some boring setters and getters for value */ 
    public int hashCode() { return value.hashCode(); } 
    public boolean equals(Object obj) { 
      if (obj != null && getClass().equals(obj.getClass()) { 
       return ((Box) obj).value.equals(value); 
      } else { return false; } 
    } 
} 

El hacer esto:

Set<Box> s = new HashSet<Box>(); 
Box b = new Box(); 
b.setValue("hello"); 
s.add(b); 
s.contains(b); // TRUE 
b.setValue("other"); 
s.contains(b); // FALSE 
s.iterator().next() == b // TRUE!!! b is in s but contains(b) returns false 

Lo que se aprende de este ejemplo es que la implementación de equals o hashCode con propiedades que se pueden cambiado (mutable) es una muy mala idea.

0

Es principalmente importante cuando se busca un objeto utilizando su valor hashCode() en una colección (es decir, HashMap, HashSet, etc.). Cada objeto devuelve un valor hashCode() diferente, por lo tanto, debe anular este método para generar consistentemente un valor hashCode basado en el estado del objeto para ayudar al algoritmo Collections a ubicar valores en la tabla hash.

Cuestiones relacionadas