Sobre hashcode, en Wikipedia:
En el lenguaje de programación Java, cada clase implícita o explícitamente proporciona un método hashcode (), que digiere los datos almacenados en una instancia de la clase en un solo valor hash (un entero firmado de 32 bits).
Hashcode extrae un entero de 32 bits basado en todos los datos almacenados en una instancia de objeto. El propósito de este entero es indicar la singularidad de la instancia. Es algo similar al código MD5, y cada archivo puede generar un código MD5 único a través del algoritmo MD5. Sin embargo, el hashcode en Java realmente no implementa el húsico para generar un hashcode único para cada objeto, y todavía hay una cierta probabilidad de duplicación.
Echemos un vistazo a la clase de objeto primero. Sabemos que la clase de objeto es la clase principal directa o indirecta de todas las clases en un programa Java y está en el punto más alto del nivel de clase. Muchos métodos comunes se definen en la clase de objetos, incluido el método hashcode del que queremos hablar, de la siguiente manera
clase pública final de la clase nativa <?> getClass (); público nativo int hashcode (); public boolean iguales (objeto obj) {return (this == obj); } public String toString () {return getClass (). getName () + "@" + integer.toHexString (hashcode ()); }Tenga en cuenta que hay un modificador nativo frente al método hashcode, lo que significa que el método hashcode se implementa en un lenguaje no java. El método específico se implementa externamente y devuelve la dirección del objeto de memoria.
En muchas clases de Java, se reescriben los métodos iguales y el hashcode. ¿Por qué es esto? La clase de cadena más común, como si definiera dos cadenas con los mismos caracteres, luego, al compararlos, el resultado que quiero debe ser igual. Si no anula los métodos iguales y hashcode, definitivamente no serán iguales, porque las direcciones de memoria de los dos objetos son diferentes.
public int hashcode () {int h = hash; if (h == 0) {int off = offset; char val [] = valor; int len = count; para (int i = 0; i <len; i ++) {H = 31*H+val [Off ++]; } hash = h; } return h; }De hecho, este código es la implementación de esta expresión matemática
S [0]*31^(N-1) + S [1]*31^(N-2) + ... + S [N-1]
S [i] es el carácter i-th de una cadena, y N es la longitud de una cadena. Entonces, ¿por qué usar 31 aquí en lugar de otros números? Java efectivo dice esto: la razón por la que se elige 31 es porque es un número primo impar. Si el multiplicador es un número uniforme y la multiplicación se desborda, la información se perderá, ya que multiplicar con 2 es equivalente a la operación de cambio. Los beneficios de usar números primos no son obvios, pero los resultados del hash se utilizan convencionalmente para calcular los resultados hash. 31 tiene una buena característica, que es usar el cambio y la resta en lugar de la multiplicación para obtener un mejor rendimiento: 31*i == (i << 5) -i. Las máquinas virtuales pueden completar automáticamente esta optimización.
Como puede ver, la clase de cadena usa su valor de valor como parámetro para calcular el hasticón, es decir, el mismo valor definitivamente tendrá el mismo valor hashcode. Esto también es fácil de entender, porque los valores del valor son los mismos, por lo que si la comparación igual también es igual, el método igual es igual, entonces el húsico debe ser igual. Al revés no es necesariamente cierto. No garantiza que el mismo hashcode debe tener el mismo objeto.
Una buena función de hash debería verse así: producir hashcodes desiguales para diferentes objetos.
En casos ideales, la función hash debería distribuir uniformemente instancias desiguales en el conjunto a todos los hashcodes posibles. Es muy difícil lograr esta situación ideal, al menos Java no lo ha logrado. Debido a que podemos ver que el hashcode se genera de manera no aleatoria, y tiene ciertas reglas, que es la ecuación matemática anterior. Podemos construir algunos que tengan el mismo hastilero pero valores de valores diferentes, por ejemplo: el húsico de AA y BB son los mismos.
El siguiente código:
clase pública main {public static void main (string [] args) {main m = new main (); System.out.println (m); System.out.println (integer.tohexstring (m.hashcode ())); Cadena a = "aa"; Cadena b = "bb"; System.out.println (a.hashcode ()); System.out.println (B.hashcode ()); }}Resultado de salida:
Main@2A139A55 2A139A55 2112 2112
En general, al reescribir la función igual, también debe reescribir la función hashcode. ¿Por qué es esto?
Echemos un vistazo a este ejemplo, creemos un empleado de clase simple
Empleado de clase pública {ID de entero privado; cadena privada FirstName; cadena privada lastname; Departamento de cuerdas privadas; public Integer getId () {return id; } public void setid (ID de entero) {this.id = id; } public String getFirstName () {return FirstName; } public void setFirstName (String FirstName) {this.firstname = firstName; } public String getLastName () {return LastName; } public void setLastName (String LastName) {this.lastName = lastName; } public String getDepartment () {Departamento de retorno; } public void setDepartment (departamento de cadenas) {this.department = departamento; }}La clase de empleados anterior solo tiene algunas propiedades muy básicas y getters y setters. Ahora considere una situación en la que necesita comparar a dos empleados.
public class EqualStest {public static void main (string [] args) {Employee e1 = new Employee (); Empleado e2 = nuevo empleado (); e1.setId (100); e2.setid (100); // Imprime falso en la consola System.out.println (e1.equals (e2)); }}No hay duda de que el programa anterior generará falso, pero de hecho, los dos objetos anteriores representan a través de un empleado. La verdadera lógica comercial espera que devuelvamos verdadero.
Para lograr esto, necesitamos reescribir el método igual.
public boolean iguales (objeto o) {if (o == null) {return false; } if (o == esto) {return true; } if (getClass ()! = O.getClass ()) {return false; } Empleado E = (Empleado) O; return (this.getID () == E.GetId ());} Agregue este método a la clase anterior y EAUQLSTEST generará verdadero.
Entonces, ¿hemos terminado? No, cambiemos el método de prueba y echemos un vistazo.
import java.util.hashset; import java.util.set; public class EqualStest {public static void main (string [] args) {Employee e1 = new Employeeee (); Empleado e2 = nuevo Empleado (); e1.SetId (100); e2.SetId (100); // imprime 'true'uts.ut.println (e1.equals (e2); Hashset <Eloza> (); Employes.Add (E1); Employs.Add (E2); // Imprime dos ObjectSSystem.out.println (Empleados);}El programa anterior genera dos resultados. Si los dos objetos de los empleados equivalen a devolver verdadero, solo se debe almacenar un objeto en el conjunto. ¿Cuál es el problema?
Olvidamos el segundo método importante hashcode (). Justo como dijo Javadoc de JDK, si reescribe el método igual (), debe reescribir el método hashcode (). Agregamos el siguiente método y el programa se ejecutará correctamente.
@Override public int hashcode () {final int prime = 31; int resultado = 1; resultado = prime * resultado + getId (); resultado de retorno; }Cosas para recordar
Intente asegurarse de que la misma propiedad del objeto se use para generar dos métodos: hashcode () y igual (). En nuestro caso, usamos ID de empleados.
El método Eqauls debe ser consistente (si el objeto no se modifica, es igual a devolver el mismo valor)
En cualquier momento, siempre que A.Equals (b), a.hashcode () debe ser igual a B.hashcode ().
Ambos deben reescribirse al mismo tiempo.
Resumir
Lo anterior se trata de la comprensión profunda de este artículo del método hashcode en Java, y espero que sea útil para todos. Los amigos interesados pueden continuar referiéndose a otros temas relacionados en este sitio. Si hay alguna deficiencia, deje un mensaje para señalarlo. ¡Gracias amigos por su apoyo para este sitio!