metodo example java equals hashcode

example - ¿Por qué necesito anular los métodos equals y hashCode en Java?



metodo hash java (29)

Recientemente leí este documento de Developer Works .

El documento trata de definir hashCode() y equals() efectiva y correcta, sin embargo, no puedo averiguar por qué necesitamos anular estos dos métodos.

¿Cómo puedo tomar la decisión de implementar estos métodos de manera eficiente?


Debe anular hashCode () en todas las clases que anulen igual a (). De lo contrario, se producirá una violación del contrato general para Object.hashCode (), que evitará que su clase funcione correctamente junto con todas las colecciones basadas en hash, incluidos HashMap, HashSet y Hashtable.


de Effective Java , por Joshua Bloch

Al definir equals() y hashCode() consistente, puede mejorar la usabilidad de sus clases como claves en colecciones basadas en hash. Como explica el documento API para hashCode: "Este método es compatible para el beneficio de tablas hash como las que proporciona java.util.Hashtable ".

La mejor respuesta a su pregunta sobre cómo implementar estos métodos de manera eficiente es sugerirle que lea el Capítulo 3 de Effective Java .


Para ayudarlo a verificar si hay objetos duplicados, necesitamos un igual personalizado y un código hash.

Dado que el código hash siempre devuelve un número, siempre es rápido recuperar un objeto utilizando un número en lugar de una clave alfabética. ¿Cómo va a hacer? Supongamos que creamos un nuevo objeto al pasar algún valor que ya está disponible en algún otro objeto. Ahora el nuevo objeto devolverá el mismo valor hash que el de otro objeto porque el valor pasado es el mismo. Una vez que se devuelve el mismo valor hash, JVM irá a la misma dirección de memoria cada vez y, en caso de que haya más de un objeto presente para el mismo valor hash, utilizará el método equals () para identificar el objeto correcto.


Añadiendo a la respuesta de @Lombo

¿Cuándo necesitarás anular equals ()?

La implementación por defecto de Object''s equals () es

public boolean equals(Object obj) { return (this == obj); }

lo que significa que dos objetos se considerarán iguales solo si tienen la misma dirección de memoria que será verdadera solo si está comparando un objeto consigo mismo.

Pero es posible que desee considerar dos objetos iguales si tienen el mismo valor para una o más de sus propiedades (consulte el ejemplo dado en la respuesta de @Lombo).

Por lo tanto, anulará equals() en estas situaciones y dará sus propias condiciones para la igualdad.

He implementado exitosamente equals () y está funcionando muy bien. ¿Entonces por qué están pidiendo que se invalide también hashCode ()?

Bien. Mientras no uses Colecciones basadas en "Hash" en tu clase definida por el usuario, está bien. Pero en algún momento en el futuro es posible que desee utilizar HashMap o HashSet y, si no override e "implementa correctamente" hashCode () , esta recopilación basada en Hash no funcionará como se esperaba.

Anular solo es igual (Suma a la respuesta de @Lombo)

myMap.put(first,someValue) myMap.contains(second); --> But it should be the same since the key are the same.But returns false!!! How?

En primer lugar, HashMap comprueba si el código de hash del second es el mismo que el first . Solo si los valores son los mismos, se procederá a verificar la igualdad en el mismo grupo.

Pero aquí el código de hash es diferente para estos 2 objetos (porque tienen una dirección de memoria diferente de la implementación predeterminada). Por lo tanto, ni siquiera le importará comprobar la igualdad.

Si tiene un punto de interrupción dentro de su método equals anulado (), no intervendría si tuvieran diferentes códigos hash. contains() comprueba hashCode() y solo si son iguales llamaría a su método equals() .

¿Por qué no podemos hacer que el HashMap compruebe la igualdad en todos los grupos? ¡Así que no hay necesidad de que anule el código hash ()!

Entonces te estás perdiendo el punto de las colecciones basadas en hash. Considera lo siguiente :

Your hashCode() implementation : intObject%9.

Las siguientes son las claves almacenadas en forma de cubos.

Bucket 1 : 1,10,19,... (in thousands) Bucket 2 : 2,20,29... Bucket 3 : 3,21,30,... ...

Digamos que desea saber si el mapa contiene la clave 10. ¿Desea buscar en todos los grupos? o ¿Quieres buscar solo un cubo?

Basado en el código de hash, usted identificaría que si 10 está presente, debe estar presente en el Cubo 1. ¡Así que solo se buscará el Cubo 1!


Considere la colección de bolas en un cubo todo en color negro. Tu trabajo es colorear esas bolas de la siguiente manera y usarlas para el juego apropiado,

Para el tenis - amarillo, rojo. Para Cricket - Blanco

Ahora el cubo tiene bolas en tres colores amarillo, rojo y blanco. Y que ahora hiciste la coloración. Solo tú sabes qué color es para qué juego.

Coloreando las bolas - Hashing. Elegir la pelota para el juego - Igual.

Si hiciste el color y alguien elige la pelota para el cricket o el tenis, ¡no les importará el color!


En pocas palabras, el método igual en la comprobación de objetos para la igualdad de referencia, donde dos instancias de su clase aún podrían ser semánticamente iguales cuando las propiedades son iguales. Esto es importante, por ejemplo, cuando coloca sus objetos en un contenedor que utiliza iguales y código hash, como HashMap y Set . Digamos que tenemos una clase como:

public class Foo { String id; String whatevs; Foo(String id, String whatevs) { this.id = id; this.whatevs = whatevs; } }

Creamos dos instancias con el mismo id :

Foo a = new Foo("id", "something"); Foo b = new Foo("id", "something else");

Sin anular iguales estamos obteniendo:

  • a.equals (b) es falso porque son dos instancias diferentes
  • a.equals (a) es cierto ya que es la misma instancia
  • b.equals (b) es cierto ya que es la misma instancia

¿Correcto? Bueno, tal vez, si esto es lo que quieres. Pero digamos que queremos que los objetos con la misma ID sean el mismo objeto, independientemente de si se trata de dos instancias diferentes. Anulamos los iguales (y hashcode):

public class Foo { String id; String whatevs; Foo(String id, String whatevs) { this.id = id; this.whatevs = whatevs; } @Override public boolean equals(Object other) { if (other instanceof Foo) { return ((Foo)other).id.equals(this.id); } } @Override public int hashCode() { return this.id.hashCode(); } }

En cuanto a la implementación de iguales y hashcode, puedo recomendar el uso de los métodos de ayuda de Guava.


Es útil cuando se utilizan objetos de valor . El siguiente es un extracto del repositorio de patrones de Portland :

Ejemplos de objetos de valor son cosas como números, fechas, dinero y cadenas. Por lo general, son objetos pequeños que se utilizan ampliamente. Su identidad se basa en su estado más que en su identidad de objeto. De esta manera, puede tener varias copias del mismo objeto de valor conceptual.

Por lo tanto, puedo tener varias copias de un objeto que representa la fecha 16 de enero de 1998. Cualquiera de estas copias será igual a la otra. Para un objeto pequeño como este, a menudo es más fácil crear objetos nuevos y moverlos en lugar de confiar en un solo objeto para representar la fecha.

Un objeto de valor siempre debe anular .equals () en Java (o = en Smalltalk). (Recuerde también anular .hashCode ()).


Estaba buscando en la explicación "Si solo anula hashCode, entonces, cuando llama a myMap.put(first,someValue) toma primero, calcula su hashCode y lo almacena en un grupo dado. Luego, cuando llama a myMap.put(first,someOtherValue) debe reemplazar primero con segundo según la Documentación del Mapa porque son iguales (según nuestra definición) ". :

Creo que la segunda vez que estamos agregando myMap , debería ser el ''segundo'' objeto como myMap.put(second,someOtherValue)


Java pone una regla que

"Si dos objetos son iguales utilizando el método de clase de objeto igual, entonces el método de código de hash debe dar el mismo valor para estos dos objetos".

Por lo tanto, si en nuestra clase anulamos equals() deberíamos anular el método hashcode() también para seguir esta regla. Ambos métodos, equals() y hashcode() , se usan en Hashtable , por ejemplo, para almacenar valores como pares clave-valor. Si anulamos uno y no el otro, existe la posibilidad de que el Hashtable no funcione como queremos, si usamos ese objeto como una clave.


Joshua Bloch dice sobre Java efectiva

Debe anular hashCode () en todas las clases que anulen igual a (). De lo contrario, se producirá una violación del contrato general para Object.hashCode (), que evitará que su clase funcione correctamente junto con todas las colecciones basadas en hash, incluidos HashMap, HashSet y Hashtable.

Intentemos entenderlo con un ejemplo de lo que sucedería si reemplazamos a equals() sin reemplazar a hashCode() e intentamos usar un Map .

Digamos que tenemos una clase como esta y que dos objetos de MyClass son iguales si su importantField es igual (con hashCode() y equals() generado por eclipse)

public class MyClass { private final String importantField; private final String anotherField; public MyClass(final String equalField, final String anotherField) { this.importantField = equalField; this.anotherField = anotherField; } public String getEqualField() { return importantField; } public String getAnotherField() { return anotherField; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((importantField == null) ? 0 : importantField.hashCode()); return result; } @Override public boolean equals(final Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; final MyClass other = (MyClass) obj; if (importantField == null) { if (other.importantField != null) return false; } else if (!importantField.equals(other.importantField)) return false; return true; } }

Anular solo equals

Si solo es equals myMap.put(first,someValue) , entonces cuando llames a myMap.put(first,someValue) hash a un bucket y cuando llamas a myMap.put(second,someOtherValue) hash a algún otro bucket (ya que tienen un hashCode diferente hashCode ). Por lo tanto, aunque son iguales, ya que no se agrupan en el mismo cubo, el mapa no se puede dar cuenta y ambos permanecen en el mapa.

Aunque no es necesario anular equals() si hashCode() , veamos qué pasaría en este caso particular en el que sabemos que dos objetos de MyClass son iguales si su campo importantField es igual pero no anula equals() .

Anular solo hashCode

Imagina que tienes esto

MyClass first = new MyClass("a","first"); MyClass second = new MyClass("a","second");

Si solo reemplaza hashCode entonces, cuando llama a myMap.put(first,someValue) , primero toma, calcula su hashCode y lo almacena en un grupo dado. Luego, cuando llama a myMap.put(second,someOtherValue) , debe reemplazar primero con segundo según la documentación del mapa porque son iguales (según el requisito comercial).

Pero el problema es que equals no se redefinió, por lo que cuando el mapa pulsa second e itera a través del cubo buscando si hay un objeto k tal que second.equals(k) sea ​​verdadero, no encontrará ninguno como second.equals(first) será false .

Espero que quede claro


Las colecciones como HashMap y HashSet utilizan un valor de HashSet de un objeto para determinar cómo se debe almacenar dentro de una colección, y el código hash se usa nuevamente para ubicar el objeto en su colección.

La recuperación de hash es un proceso de dos pasos:

  1. Encuentra el cubo correcto (usando hashCode() )
  2. Busque en el cubo el elemento correcto (usando equals() )

Aquí hay un pequeño ejemplo de por qué debemos anular equals() y hashcode() .

Considere una clase de Employee que tiene dos campos: edad y nombre.

public class Employee { String name; int age; public Employee(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public boolean equals(Object obj) { if (obj == this) return true; if (!(obj instanceof Employee)) return false; Employee employee = (Employee) obj; return employee.getAge() == this.getAge() && employee.getName() == this.getName(); } // commented /* @Override public int hashCode() { int result=17; result=31*result+age; result=31*result+(name!=null ? name.hashCode():0); return result; } */ }

Ahora cree una clase, inserte el objeto Employee en un HashSet y pruebe si ese objeto está presente o no.

public class ClientTest { public static void main(String[] args) { Employee employee = new Employee("rajeev", 24); Employee employee1 = new Employee("rajeev", 25); Employee employee2 = new Employee("rajeev", 24); HashSet<Employee> employees = new HashSet<Employee>(); employees.add(employee); System.out.println(employees.contains(employee2)); System.out.println("employee.hashCode(): " + employee.hashCode() + " employee2.hashCode():" + employee2.hashCode()); } }

Se imprimirá lo siguiente:

false employee.hashCode(): 321755204 employee2.hashCode():375890482

Ahora hashcode() método hashcode() , ejecute el mismo y la salida sería:

true employee.hashCode(): -938387308 employee2.hashCode():-938387308

Ahora, ¿puedes ver por qué si dos objetos se consideran iguales, su hashcode s también debe ser igual? De lo contrario, nunca podría encontrar el objeto, ya que el método de código de hash predeterminado en la clase Objeto prácticamente siempre tiene un número único para cada objeto, incluso si el método equals() está anulado de tal manera que dos o más objetos se consideran iguales. No importa cuán iguales sean los objetos si sus códigos de hash no reflejan eso. Así que una vez más: si dos objetos son iguales, sus hashcode s también deben ser iguales.


Los métodos igual y hashcode se definen en la clase de objeto. De forma predeterminada, si el método igual devuelve verdadero, entonces el sistema irá más allá y comprobará el valor del código hash. Si el código hash de los 2 objetos también es el mismo, los objetos se considerarán iguales. Por lo tanto, si solo reemplaza el método de iguales, entonces aunque el método de iguales de reemplazo indica que 2 objetos son iguales, el código de hash definido por el sistema puede no indicar que los 2 objetos son iguales. Así que también tenemos que anular el código hash.


Ok, déjame explicarte el concepto en palabras muy simples.

En primer lugar, desde una perspectiva más amplia, tenemos colecciones, y el hashmap es una de las estructuras de datos de las colecciones.

Para entender por qué tenemos que anular el método de ambos códigos de hash y hash, si es necesario primero entender qué es hashmap y qué hace.

Un mapa de hash es una estructura de datos que almacena pares de valores clave de datos en forma de matriz. Digamos a [], donde cada elemento en ''a'' es un par de valores clave.

Además, cada índice de la matriz anterior puede ser una lista enlazada que tiene más de un valor en un índice.

Ahora, ¿por qué se utiliza un hashmap? Si tenemos que buscar entre una matriz grande, entonces la búsqueda a través de cada una de ellas no será eficiente, por lo que la técnica de hash nos dice que pre procesamos la matriz con cierta lógica y agrupamos los elementos según esa lógica, es decir, Hashing

por ejemplo: tenemos la matriz 1,2,3,4,5,6,7,8,9,10,10 y aplicamos una función hash mod 10, por lo que se agrupará 1,11. Entonces, si tuviéramos que buscar 11 en la matriz anterior, tendríamos que iterar la matriz completa, pero cuando la agrupamos limitamos nuestro alcance de iteración, mejorando así la velocidad. La estructura de datos utilizada para almacenar toda la información anterior se puede considerar como una matriz 2D para la simplicidad

Ahora, aparte del hashmap anterior, también se dice que no agregará ningún Duplicado en él. Y esta es la razón principal por la que debemos anular los iguales y el código hash

Entonces, cuando se dice que explica el funcionamiento interno del hashmap, necesitamos encontrar qué métodos tiene el hashmap y cómo sigue las reglas anteriores que expliqué anteriormente.

por lo tanto, el hashmap tiene un método llamado put (K, V), y de acuerdo con el hashmap, debe seguir las reglas anteriores de distribución eficiente de la matriz y no agregar duplicados

así que lo que se dice es que primero generará el código hash para la clave dada para decidir a qué índice debe ir el valor. Si no hay nada en ese índice, el nuevo valor se agregará allí, si algo ya está presente allí. luego, el nuevo valor debe agregarse después del final de la lista enlazada en ese índice. pero recuerde que no se deben agregar duplicados según el comportamiento deseado del mapa hash. así que digamos que tienes dos objetos enteros aa = 11, bb = 11. Como cada objeto derivado de la clase de objeto, la implementación predeterminada para comparar dos objetos es que compara la referencia y no los valores dentro del objeto. Entonces, en el caso anterior, aunque semánticamente igual fallará la prueba de igualdad, y existe la posibilidad de que existan dos objetos con el mismo código hash y los mismos valores, creando así duplicados. Si anulamos, podríamos evitar agregar duplicados. También puede consultar Detalle trabajando.

import java.util.HashMap; public class Employee { String name; String mobile; public Employee(String name,String mobile) { this.name=name; this.mobile=mobile; } @Override public int hashCode() { System.out.println("calling hascode method of Employee"); String str=this.name; Integer sum=0; for(int i=0;i<str.length();i++){ sum=sum+str.charAt(i); } return sum; } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub System.out.println("calling equals method of Employee"); Employee emp=(Employee)obj; if(this.mobile.equalsIgnoreCase(emp.mobile)){ System.out.println("returning true"); return true; }else{ System.out.println("returning false"); return false; } } public static void main(String[] args) { // TODO Auto-generated method stub Employee emp=new Employee("abc", "hhh"); Employee emp2=new Employee("abc", "hhh"); HashMap<Employee, Employee> h=new HashMap<>(); //for (int i=0;i<5;i++){ h.put(emp, emp); h.put(emp2, emp2); //} System.out.println("----------------"); System.out.println("size of hashmap: "+h.size()); } }


Para utilizar nuestros propios objetos de clase como claves en colecciones como HashMap, Hashtable, etc., deberíamos anular ambos métodos (hashCode () y equals ()) teniendo en cuenta el funcionamiento interno de la colección. De lo contrario, conduce a resultados erróneos que no se esperan.


Porque si no los reemplaza, utilizará la implementación predeterminada en Objeto.

Dado que los valores de igualdad de hasity y hascode generalmente requieren el conocimiento de lo que constituye un objeto, en general necesitarán ser redefinidos en su clase para tener un significado tangible.


Suponga que tiene la clase (A) que agrega otras dos (B) (C), y necesita almacenar instancias de (A) dentro de la tabla hash. La implementación predeterminada solo permite distinguir instancias, pero no por (B) y (C). Por lo tanto, dos instancias de A podrían ser iguales, pero el valor predeterminado no le permitiría compararlas de manera correcta.


hashCode() :

Si solo anula el método de código hash, no sucederá nada. Porque siempre devuelve un nuevo hashCode para cada objeto como una clase de objeto.

equals() :

Si solo reemplaza el método igual, a.equals(b) es verdadero significa que el hashCode de a y b debe ser el mismo pero no debe suceder. Porque no hashCode método hashCode .

Nota: el método hashCode() de la clase Object siempre devuelve un nuevo hashCode para cada objeto.

Entonces, cuando necesite usar su objeto en la colección basada en hash, debe reemplazar tanto equals() como hashCode() .


hashCode()El método se usa para obtener un entero único para un objeto dado. Este entero se utiliza para determinar la ubicación del depósito, cuando este objeto debe almacenarse en alguna HashTable, HashMapcomo la estructura de datos. De forma predeterminada, el hashCode()método de Object devuelve y la representación entera de la dirección de memoria donde se almacena el objeto.

El hashCode()método de los objetos se utiliza cuando los insertamos en a HashTable, HashMapo HashSet. Más sobre HashTablesen Wikipedia.org para referencia.

Para insertar cualquier entrada en la estructura de datos del mapa, necesitamos tanto la clave como el valor. Si tanto la clave como los valores son tipos de datos definidos por el usuario, hashCode()la clave determinará dónde almacenar el objeto internamente. Cuando sea necesario buscar también el objeto desde el mapa, el código hash de la clave determinará dónde buscar el objeto.

El código hash solo apunta a cierta "área" (o lista, grupo, etc.) internamente. Dado que diferentes objetos clave podrían tener el mismo código hash, el código hash en sí mismo no es garantía de que se encuentre la clave correcta. La HashTablecontinuación itera esta área (todas las claves con el mismo código hash) y utiliza la clave del equals()método para encontrar la clave correcta. Una vez que se encuentra la clave correcta, se devuelve el objeto almacenado para esa clave.

Entonces, como podemos ver, se utiliza una combinación de los métodos hashCode()y equals()al almacenar y buscar objetos en un archivo HashTable.

NOTAS:

  1. Siempre use los mismos atributos de un objeto para generar hashCode()y equals()ambos. Como en nuestro caso, hemos utilizado id de empleado.

  2. equals() debe ser consistente (si los objetos no se modifican, entonces debe seguir devolviendo el mismo valor).

  3. Cuando sea a.equals(b), entonces a.hashCode()debe ser igual que b.hashCode().

  4. Si anula uno, debe anular el otro.

http://parameshk.blogspot.in/2014/10/examples-of-comparable-comporator.html


Bah - "Debes reemplazar hashCode () en cada clase que reemplaza a equals ()".

[de Effective Java, por Joshua Bloch?]

¿No es esto al revés? La anulación de código hash probablemente implica que estás escribiendo una clase de clave hash, pero la anulación de iguales ciertamente no lo hace. Hay muchas clases que no se utilizan como claves hash, pero que quieren un método de prueba de igualdad lógica por alguna otra razón. Si elige "es igual" para él, puede que se le solicite escribir una implementación de código hash por una aplicación demasiado entusiasta de esta regla. Todo lo que se logra es agregar código no probado en el código base, un malvado que espera hacer tropezar a alguien en el futuro. También escribir código que no necesitas es anti-ágil. Es simplemente incorrecto (y una ide generada probablemente será incompatible con tus iguales hechos a mano).

¿Seguramente deberían haber ordenado una Interfaz en objetos escritos para ser utilizados como claves? Independientemente, Object nunca debería haber proporcionado el código de hash predeterminado () y es igual a () imho. Probablemente es alentador muchas colecciones de hash roto.

Pero de todos modos, creo que la "regla" está escrita al revés. Mientras tanto, seguiré evitando el uso de "iguales" para los métodos de prueba de igualdad :-(


La identidad no es igualdad.

  • operador igual == identidad de prueba.
  • equals(Object obj) método equals(Object obj) compara la prueba de igualdad (es decir, debemos indicar la igualdad anulando el método)

¿Por qué necesito anular los métodos equals y hashCode en Java?

Primero tenemos que entender el uso del método equals.

Para identificar las diferencias entre dos objetos necesitamos anular el método de iguales.

Por ejemplo:

Customer customer1=new Customer("peter"); Customer customer2=customer1; customer1.equals(customer2); // returns true by JVM. i.e. both are refering same Object ------------------------------ Customer customer1=new Customer("peter"); Customer customer2=new Customer("peter"); customer1.equals(customer2); //return false by JVM i.e. we have two different peter customers. ------------------------------ Now I have overriden Customer class equals method as follows: @Override public boolean equals(Object obj) { if (this == obj) // it checks references return true; if (obj == null) // checks null return false; if (getClass() != obj.getClass()) // both object are instances of same class or not return false; Customer other = (Customer) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) // it again using bulit in String object equals to identify the difference return false; return true; } Customer customer1=new Customer("peter"); Customer customer2=new Customer("peter"); Insteady identify the Object equality by JVM, we can do it by overring equals method. customer1.equals(customer2); // returns true by our own logic

Ahora el método hashCode puede entender fácilmente.

hashCode produce un entero para almacenar objetos en estructuras de datos como HashMap , HashSet .

Supongamos que tenemos un método de anulación igual al del Customer como se Customer anteriormente,

customer1.equals(customer2); // returns true by our own logic

Mientras trabajamos con la estructura de datos cuando almacenamos objetos en cubos (bucket es un nombre elegante para la carpeta). Si utilizamos la técnica hash incorporada, para los dos clientes anteriores genera dos hashcode diferentes. Así que estamos almacenando el mismo objeto idéntico en dos lugares diferentes. Para evitar este tipo de problemas, deberíamos anular el método hashCode también sobre la base de los siguientes principios.

  • Las instancias no iguales pueden tener el mismo código hash.
  • Las instancias iguales deben devolver el mismo código hash.

Métodos iguales y Hashcode en Java

Son métodos de la clase java.lang.Object que es la superclase de todas las clases (clases personalizadas también y otras definidas en la API de Java).

Implementación:

booleanos públicos iguales (objeto obj)

public int hashCode ()

booleanos públicos iguales (objeto obj)

Este método simplemente verifica si dos referencias de objeto x e y se refieren al mismo objeto. Es decir, comprueba si x == y.

Es reflexivo: para cualquier valor de referencia x, x.equals (x) debe devolver verdadero.

Es simétrico: para cualquier valor de referencia x e y, x.equals (y) debe devolver verdadero si y solo si y.equals (x) devuelve verdadero.

Es transitivo: para cualquier valor de referencia x, y y z, si x.equals (y) devuelve true y e.equals (z) devuelve true, entonces x.equals (z) debe devolver true.

Es consistente: para cualquier valor de referencia x e y, las múltiples invocaciones de x.equals (y) devuelven consistentemente verdadero o devuelven constantemente falso, siempre que no se modifique la información utilizada en comparaciones iguales en el objeto.

Para cualquier valor de referencia que no sea nulo x, x.equals (nulo) debe devolver falso.

public int hashCode ()

Este método devuelve el valor del código hash para el objeto en el que se invoca este método. Este método devuelve el valor del código hash como un entero y es compatible con el beneficio de las clases de colección basadas en hash, como Hashtable, HashMap, HashSet, etc. Este método debe anularse en todas las clases que invaliden el método equals.

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 método hashCode debe devolver constantemente el mismo número entero, siempre que no se modifique la información utilizada en comparaciones iguales en el objeto.

No es necesario que este número entero permanezca consistente de una ejecución de una aplicación a otra ejecución de la misma aplicación.

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.

No es necesario que si dos objetos son desiguales según el método equals (java.lang.Object), 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 las tablas hash.

Los objetos iguales deben producir el mismo código hash siempre que sean iguales, sin embargo, los objetos desiguales no necesitan producir códigos hash distintos.

Recursos:

JavaRanch

Picture


Cuando desee almacenar y recuperar su objeto personalizado como una clave en el Mapa, entonces siempre debe anular los iguales y el código hash en su Objeto personalizado. P.ej:

Person p1 = new Person("A",23); Person p2 = new Person("A",23); HashMap map = new HashMap(); map.put(p1,"value 1"); map.put(p2,"value 2");

Aquí, p1 y p2 se considerarán como un solo objeto y el maptamaño será solo 1 porque son iguales.


En mi humilde opinión, es como dice la regla: si dos objetos son iguales, entonces deberían tener el mismo hash, es decir, los objetos iguales deberían producir valores de hash iguales.

Dado lo anterior, el valor predeterminado es igual a () en Objeto == que hace una comparación en la dirección, hashCode () devuelve la dirección en entero (hash en la dirección real) que es distinta para Objeto distinto.

Si necesita usar los Objetos personalizados en las colecciones basadas en Hash, debe reemplazar ambos equals () y hashCode (), ejemplo. Si quiero mantener el HashSet de los Objetos de Employee, si no uso un hashCode más fuerte y es igual a Es posible que termine anulando los dos Objetos de empleado diferentes, esto sucede cuando uso la edad como hashCode (), sin embargo, debería usar el valor único que puede ser el ID de empleado.


1) El error común se muestra en el siguiente ejemplo.

public class Car { private String color; public Car(String color) { this.color = color; } public boolean equals(Object obj) { if(obj==null) return false; if (!(obj instanceof Car)) return false; if (obj == this) return true; return this.color.equals(((Car) obj).color); } public static void main(String[] args) { Car a1 = new Car("green"); Car a2 = new Car("red"); //hashMap stores Car type and its quantity HashMap<Car, Integer> m = new HashMap<Car, Integer>(); m.put(a1, 10); m.put(a2, 20); System.out.println(m.get(new Car("green"))); } }

el coche verde no se encuentra

2. Problema causado por hashCode ()

El problema es causado por el método no reemplazado hashCode(). El contrato entre equals()y hashCode()es:

  1. Si dos objetos son iguales, entonces deben tener el mismo código hash.
  2. Si dos objetos tienen el mismo código hash, pueden o no ser iguales.

    public int hashCode(){ return this.color.hashCode(); }


Ambos métodos se definen en la clase Object. Y ambos están en su implementación más sencilla. Entonces, cuando necesite agregar más implementación a estos métodos, entonces tendrá que anular en su clase.

Por ejemplo: el método equals () en el objeto solo verifica su igualdad en la referencia. Por lo tanto, si necesita comparar su estado, entonces puede anularlo como se hace en la clase String.


En el siguiente ejemplo, si comenta la anulación de iguales o hashcode en la clase Persona, este código no buscará la orden de Tom. El uso de la implementación predeterminada de hashcode puede causar fallas en las búsquedas de tabla hash.

Lo que tengo a continuación es un código simplificado que extrae el pedido de personas por persona. La persona está siendo utilizada como una clave en la tabla hash.

public class Person { String name; int age; String socialSecurityNumber; public Person(String name, int age, String socialSecurityNumber) { this.name = name; this.age = age; this.socialSecurityNumber = socialSecurityNumber; } @Override public boolean equals(Object p) { //Person is same if social security number is same if ((p instanceof Person) && this.socialSecurityNumber.equals(((Person) p).socialSecurityNumber)) { return true; } else { return false; } } @Override public int hashCode() { //I am using a hashing function in String.java instead of writing my own. return socialSecurityNumber.hashCode(); } } public class Order { String[] items; public void insertOrder(String[] items) { this.items=items; } } import java.util.Hashtable; public class Main { public static void main(String[] args) { Person p1=new Person("Tom",32,"548-56-4412"); Person p2=new Person("Jerry",60,"456-74-4125"); Person p3=new Person("Sherry",38,"418-55-1235"); Order order1=new Order(); order1.insertOrder(new String[]{"mouse","car charger"}); Order order2=new Order(); order2.insertOrder(new String[]{"Multi vitamin"}); Order order3=new Order(); order3.insertOrder(new String[]{"handbag", "iPod"}); Hashtable<Person,Order> hashtable=new Hashtable<Person,Order>(); hashtable.put(p1,order1); hashtable.put(p2,order2); hashtable.put(p3,order3); //The line below will fail if Person class does not override hashCode() Order tomOrder= hashtable.get(new Person("Tom", 32, "548-56-4412")); for(String item:tomOrder.items) { System.out.println(item); } } }


La clase de cadena y las clases de envoltorio tienen una implementación equals()y hashCode()métodos diferentes a la clase de objeto. El método equals () de la clase de objeto compara las referencias de los objetos, no los contenidos. El método hashCode () de la clase Object devuelve un código hash distinto para cada objeto, independientemente de que el contenido sea el mismo.

Provoca problemas cuando usa la colección de mapas y la clave es de tipo persistente, tipo StringBuffer / builder. Dado que no anulan equals () y hashCode () a diferencia de la clase String, equals () devolverá false cuando se comparan dos objetos diferentes, aunque ambos tengan el mismo contenido. Hará que el hashMap almacene las mismas claves de contenido. Almacenar las mismas claves de contenido significa que está violando la regla de Mapa porque el Mapa no permite claves duplicadas en absoluto. Por lo tanto, reemplaza los métodos equals () y hashCode () en su clase y proporciona la implementación (el IDE puede generar estos métodos) para que funcionen igual que String''s equals () y hashCode () y evite las mismas claves de contenido.

Debe anular el método hashCode () junto con equals () porque equals () funciona de acuerdo con el código hash.

Además, al reemplazar el método hashCode () junto con equals () se ayuda a intactar el contrato equals () - hashCode (): "Si dos objetos son iguales, deben tener el mismo código hash".

¿Cuándo necesitas escribir una implementación personalizada para hashCode ()?

Como sabemos, el funcionamiento interno de HashMap se basa en el principio de Hashing. Hay ciertos grupos donde se almacenan los conjuntos de entradas. Puede personalizar la implementación de hashCode () de acuerdo con sus requisitos para que los mismos objetos de categoría puedan almacenarse en el mismo índice. cuando almacena los valores en la colección Map utilizando el put(k,v)método, la implementación interna de put () es:

put(k, v){ hash(k); index=hash & (n-1); }

Significa, genera un índice y el índice se genera en función del código hash de un objeto clave en particular. Así que haga que este método genere código hash de acuerdo con sus requisitos, ya que los mismos conjuntos de entradas de código hash se almacenarán en el mismo grupo o índice.

¡Eso es!


La razón detrás de esto: cuando sus campos de objeto pueden ser nulos, la implementación de Object.equals puede ser una molestia, ya que debe verificar por separado si no hay nulos. El uso de Objects.equal le permite realizar verificaciones iguales de manera nula-sensible, sin arriesgar una NullPointerException.Objects.equal("a", "a"); // returns true Objects.equal(null, "a"); // returns false Objects.equal("a", null); // returns false Objects.equal(null, null); // returns true


class A { int i; // Hashing Algorithm if even number return 0 else return 1 // Equals Algorithm, if i = this.i return true else false }

  • put (''clave'', ''valor'') calculará el valor de hash usando hashCode() para determinar el depósito y utiliza el método equals() para determinar si el valor ya está presente en el depósito. Si no se agregará, se reemplazará con el valor actual.
  • get (''key'') usará hashCode() para encontrar la Entrada (bucket) primero y equals() para encontrar el valor en Entry

si ambos están anulados,

Mapa <A>

Map.Entry 1 --> 1,3,5,... Map.Entry 2 --> 2,4,6,...

si es igual no se anula

Mapa <A>

Map.Entry 1 --> 1,3,5,...,1,3,5,... // Duplicate values as equals not overridden Map.Entry 2 --> 2,4,6,...,2,4,..

Si hashCode no se anula

Mapa <A>

Map.Entry 1 --> 1 Map.Entry 2 --> 2 Map.Entry 3 --> 3 Map.Entry 4 --> 1 Map.Entry 5 --> 2 Map.Entry 6 --> 3 // Same values are Stored in different hasCodes violates Contract 1 So on...

HashCode Equal Contract

  1. Dos claves iguales según el método igual deberían generar el mismo código hash
  2. Las dos claves que generan el mismo código hash no necesitan ser iguales (en el ejemplo anterior, todos los números pares generan el mismo código hash)

public class Employee { private int empId; private String empName; public Employee(int empId, String empName) { super(); this.empId = empId; this.empName = empName; } public int getEmpId() { return empId; } public void setEmpId(int empId) { this.empId = empId; } public String getEmpName() { return empName; } public void setEmpName(String empName) { this.empName = empName; } @Override public String toString() { return "Employee [empId=" + empId + ", empName=" + empName + "]"; } @Override public int hashCode() { return empId + empName.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (!(this instanceof Employee)) { return false; } Employee emp = (Employee) obj; return this.getEmpId() == emp.getEmpId() && this.getEmpName().equals(emp.getEmpName()); } }

Clase de prueba

public class Test { public static void main(String[] args) { Employee emp1 = new Employee(101,"Manash"); Employee emp2 = new Employee(101,"Manash"); Employee emp3 = new Employee(103,"Ranjan"); System.out.println(emp1.hashCode()); System.out.println(emp2.hashCode()); System.out.println(emp1.equals(emp2)); System.out.println(emp1.equals(emp3)); } }

En Object Class equals (Object obj) se usa para comparar la comparación de direcciones, por eso, cuando en la clase de prueba, si compara dos objetos, se iguala al método que da falso, pero cuando anula el código hash (), puede comparar el contenido y dar el resultado adecuado.