Relación entre hashCode y el método equals en Java

Leí en muchos lugares diciendo que mientras que el método de hashCode en Java, debería anular hashCode método de hashCode , de lo contrario, está “violando el contrato”.

Pero hasta ahora no he enfrentado ningún problema si anulo solo el método igual, pero no el método hashCode.

¿Cuál es el contrato? ¿Y por qué no estoy enfrentando ningún problema cuando estoy violando el contrato? ¿En qué caso enfrentaré un problema si no he reemplazado el método hashCode?

El problema que tendrá es con colecciones donde la unicidad de los elementos se calcula de acuerdo con ambos .equals() y .hashCode() , por ejemplo, claves en un HashMap .

Como su nombre lo indica, se basa en tablas hash, y los cubos hash son una función del .hashCode() del objeto.

Si tienes dos objetos que son .equals() , pero tienen diferentes códigos hash, ¡pierdes!

La parte del contrato aquí que es importante es: los objetos que son .equals() DEBEN tener el mismo .hashCode() .

Todo esto está documentado en javadoc para Object . Y Joshua Bloch dice que debes hacerlo en Java efectivo . Basta de charla.

De acuerdo con el documento, la implementación predeterminada de hashCode devolverá un número entero que difiere para cada objeto

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

Sin embargo, algunas veces quiere que el código hash sea el mismo para diferentes objetos que tienen el mismo significado. Por ejemplo

 Student s1 = new Student("John", 18); Student s2 = new Student("John", 18); s1.hashCode() != s2.hashCode(); // With the default implementation of hashCode 

Este tipo de problema se producirá si utiliza una estructura de datos hash en el marco de recostackción, como HashTable, HashSet. Especialmente con colecciones como HashSet, terminarás teniendo elementos duplicados y violarás el contrato establecido.

Sí, debe ser anulado. Si crees que debes anular equals() , entonces debes anular hashCode() y viceversa. El contrato general de hashCode () es:

  1. Cada vez que se invoca en el mismo objeto más de una vez durante la ejecución de una aplicación Java, el método hashCode debe devolver el mismo entero de forma consistente, 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.

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

  3. No es necesario que si dos objetos son desiguales de acuerdo con el método equals (java.lang.Object), al invocar el método hashCode en cada uno de los dos objetos se produzcan resultados enteros distintos. Sin embargo, el progtwigdor debe tener en cuenta que la producción de resultados enteros distintos para objetos desiguales puede mejorar el rendimiento de hashtables.

Ver JavaDoc de java.lang.Object

En hashCode() dice:

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

(Énfasis por mí).

Si solo reemplaza equals() y no hashCode() su clase infringe este contrato.

Esto también se dice en el método JavaDoc del método equals() :

Tenga en cuenta que generalmente es necesario anular el método hashCode siempre que se anule este método, a fin de mantener el contrato general para el método hashCode , que establece que los objetos iguales deben tener códigos hash iguales.

Un contrato es: si dos objetos son iguales, entonces deben tener el mismo código hash y si dos objetos no son iguales, entonces pueden tener o no el mismo código hash.

Intente usar su objeto como clave en HashMap (editado después del comentario de joachim-sauer), y comenzará a enfrentar problemas. Un contrato es una guía, no algo forzado sobre usted.

El contrato es que si obj1.equals(obj2) entonces obj1.hashCode() == obj2.hashCode() , es principalmente por motivos de rendimiento, ya que los mapas utilizan principalmente el método hashCode para comparar las claves de las entradas.

Eche un vistazo a Hashtables , Hashmaps , HashSets , etc. Todos almacenan la clave hash como sus llaves. Al invocar get(Object key) se genera el hash del parámetro y la búsqueda en los hash dados.

Cuando no sobrescribe hashCode() y se ha cambiado la instancia de la clave (por ejemplo, una cadena simple que no importa en absoluto), hashCode() podría dar como resultado 2 hashCode() diferentes para el mismo objeto, lo que resulta en no encontrar su clave dada en map.get() .