Creo que la mayoría de las personas están familiarizadas con la estructura de datos de la tabla hash, y en muchos lugares, las tablas hash se utilizan para mejorar la eficiencia de búsqueda. Hay un método en la clase de objetos de Java:
público nativo int hashcode ();
De acuerdo con la declaración de este método, se puede ver que el método devuelve un valor numérico de tipo int y es un método local, por lo que no se da una implementación específica en la clase de objeto.
¿Por qué la clase de objeto necesita tal método? ¿Cuál es su función? Hoy discutiremos el método hashcode en detalle.
1. La función del método hashcode
Para los lenguajes de programación que contienen tipos de contenedores, hashcode está básicamente involucrado. Lo mismo es cierto en Java. La función principal del método hashcode es funcionar normalmente con conjuntos basados en hash, tales conjuntos de hash incluyen hashset, hashmap y hashtable.
¿Por qué lo dices? Considere una situación en la que cuando se inserta un objeto en una colección, ¿cómo determinar si el objeto ya existe en la colección? (Nota: los elementos duplicados no están permitidos en la colección)
Quizás la mayoría de la gente piense en llamar al método igual para comparar uno por uno, y este método es realmente factible. Sin embargo, si ya hay diez mil datos o más datos en el conjunto, si el método igual se usa para comparar uno por uno, la eficiencia será inevitablemente un problema. En este momento, se refleja la función del método hashcode. Cuando se agregará un nuevo objeto a la colección, el método hashcode del objeto se llama primero para obtener el valor hashcode correspondiente. De hecho, en la implementación específica de HASHMAP, se utilizará una tabla para guardar el valor de hashcode del objeto que se ha guardado. Si no hay valor hashcode en la tabla, se puede almacenar directamente sin ninguna comparación. Si el valor hashcode existe, su método igual se llama a comparar con el nuevo elemento. Si lo mismo es verdadero, no se almacenarán otras direcciones. Por lo tanto, hay un problema de resolución de conflictos aquí. De esta manera, el número de veces que el método igual se llama en realidad se reduce considerablemente. En pocas palabras: el método hashcode en Java asigna la información relacionada con el objeto (como la dirección de almacenamiento del objeto, el campo del objeto, etc.) en un valor numérico de acuerdo con ciertas reglas, y este valor se llama valor hash. El siguiente código es la implementación específica del método PUT en java.util.hashmap:
public v put (k key, v value) {if (key == null) return putfornullkey (valor); int hash = hash (key.hashcode ()); int i = indexfor (hash, table.length); para (entrada <k, v> e = table [i]; e! = null; e = e.next) {objeto k; if (e.hash == hash && ((k = e.key) == key || key.equals (k))) {v OldValue = e.Value; e.value = valor; E.RecordAccess (esto); devolver OldValue; }} modcount ++; Addentry (hash, clave, valor, i); regresar nulo; } El método Put se usa para agregar un nuevo elemento al hashmap. A partir de la implementación específica del método PUT, podemos saber que el método hashcode se llamará primero para obtener el valor hashcode del elemento y luego verificar si el valor hashcode existe en la tabla. Si existe, llame al método igual para redeterminar si el elemento existe. Si existe, actualice el valor del valor, de lo contrario, agregue el nuevo elemento al hashmap. A partir de aquí, podemos ver que el método hashcode existe para reducir el número de llamadas al método igual y, por lo tanto, mejorar la eficiencia del programa.
Algunos amigos piensan erróneamente que, por defecto, hashcode devuelve la dirección de almacenamiento del objeto. De hecho, esta opinión está incompleta. Es cierto que algunos JVM devuelven directamente la dirección de almacenamiento del objeto cuando se implementan, pero este no es el caso la mayor parte del tiempo. Solo se puede decir que la dirección de almacenamiento puede estar relacionada con ella. La siguiente es la implementación de la generación de valores hash hash en Hotspot JVM:
static inline intptr_t get_next_hash (hilo * self, oop obj) {intptr_t value = 0; if (hashcode == 0) {// Este formulario utiliza un RNG Global Park-Miller sin vigilancia, // Por lo tanto, es posible que dos hilos corran y generen el mismo RNG. // En el sistema MP tendremos mucho acceso RW a un global, por lo que el mecanismo // induce mucho tráfico de coherencia. valor = os :: random (); } else if (hashcode == 1) {// Esta variación tiene la propiedad de ser estable (ideMPotent) // entre operaciones STW. Esto puede ser útil en algunos de los esquemas de sincronización 1-0 //. intptr_t addrbits = intptr_t (obj) >> 3; valor = addrBits ^ (addrbits >> 5) ^ gvars.stwrandom; } else if (hashcode == 2) {valor = 1; // para pruebas de sensibilidad} else if (hashcode == 3) {value = ++ gvars.hcSequence; } else if (hashcode == 4) {value = intptr_t (obj); } else {// Esquema XOR-Shift de Marsaglia con estado específico de hilo // Esta es probablemente la mejor implementación general: probablemente haremos esto el valor predeterminado en futuras versiones. sin firmar t = self-> _ hashstatex; t ^= (t << 11); Self-> _ hashstatex = self-> _ hashstatey; Self-> _ hashstatey = self-> _ hashstatez; Self-> _ hashstatez = self-> _ hashstatew; Unsigned v = self-> _ hashstatew; v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)); Self-> _ hashstatew = v; valor = v; } valor & = markopdesc :: hash_mask; if (valor == 0) valor = 0xbad; Afirmar (valor! = Markoopdesc :: no_hash, "invariante"); Tevent (hashcode: generar); valor de retorno;}Esta implementación se encuentra en el archivo HotSpot/Src/Share/VM/Runtime/Synchronizer.cpp.
Por lo tanto, algunas personas pueden decir que ¿podemos juzgar directamente si dos objetos son iguales en función del valor de hashcode? Ciertamente no es posible, porque los diferentes objetos pueden generar el mismo valor de hashcode. Aunque no es posible juzgar si dos objetos son iguales en función del valor de hashcode, puede juzgar directamente que los dos objetos no son iguales en función del valor hashcode. Si los valores hashcode de los dos objetos no son iguales, deben ser dos objetos diferentes. Si desea determinar si dos objetos son realmente iguales, debe usar el método igual.
Es decir, para dos objetos, si el resultado obtenido llamando al método igual es verdadero, los valores hashcode de los dos objetos deben ser iguales;
Si el resultado obtenido por el método igual es falso, los valores hashcode de los dos objetos pueden no ser diferentes;
Si los valores hashcode de los dos objetos no son iguales, el resultado obtenido por el método igual debe ser falso;
Si los valores hashcode de los dos objetos son iguales, se desconoce el resultado obtenido por el método igual.
2. Método Equals y Método Hashcode
En algunos casos, al diseñar una clase, los programadores deben reescribir el método igual, como la clase de cadena, pero asegúrese de tener en cuenta que al reescribir el método igual, deben reescribir el método hashcode. ¿Por qué lo dices?
Veamos un ejemplo a continuación:
paquete com.cxh.test1; import java.util.hashmap; import java.util.hashset; import java.util.set; clase de personas {nombre de cadena privada; edad privada int; Public People (Nombre de cadena, int Age) {this.name = name; this.age = edad; } public void setAge (int Age) {this.age = edad; } @Override public boolean iguales (object obj) {// TODO Método Generado automático Devuelve this.name.equals (((People) obj) .name) && this.age == ((personas) obj) .age; }} clase pública Main {public static void main (string [] args) {People P1 = New People ("Jack", 12); System.out.println (p1.hashcode ()); Hashmap <People, Integer> Hashmap = New Hashmap <People, Integer> (); Hashmap.put (P1, 1); System.out.println (Hashmap.get (New People ("Jack", 12));}}Aquí solo he reescribido el método igual, lo que significa que si dos objetos de personas tienen el mismo nombre y edad, se consideran la misma persona.
La intención original de este código es generar el resultado de "1", pero de hecho genera "NULL". ¿Por qué? La razón es que olvida reescribir el método hashcode mientras reescribe el método igual.
Aunque se determinan que dos objetos con el mismo nombre y edad se determinan lógicamente iguales (similar a la clase de cadena), debe saber que, por defecto, el método hashcode asigna la dirección de almacenamiento del objeto. Entonces no es sorprendente que el resultado de salida del código anterior sea "nulo". La razón es muy simple, el objeto apuntado por P1 y
System.out.println (Hashmap.get (New People ("Jack", 12)); Las nuevas personas ("Jack", 12) en esta oración genera dos objetos, y sus direcciones de almacenamiento deben ser diferentes. La siguiente es la implementación específica del método GET de Hashmap:
public v get (clave de objeto) {if (key == null) return getFornullKey (); int hash = hash (key.hashcode ()); para (entrada <k, v> e = table [indexfor (hash, table.length)]; e! = null; e = e.next) {objeto k; if (e.hash == hash && ((k = e.key) == key || key.equals (k))) return e.value; } return null; }Por lo tanto, cuando el hashmap se usa para obtener, porque los valores hashcdoe obtenidos son diferentes (tenga en cuenta que el código anterior puede obtener el mismo valor de hastos en algunos casos, pero la probabilidad es relativamente pequeña, porque aunque las direcciones de almacenamiento de los dos objetos son diferentes, es posible obtener el mismo valor de hashcode), por lo que el bucle FOR no se ejecutará en el método de obtener directamente.
Por lo tanto, si desea que el código anterior genere el resultado "1", es muy simple. Solo necesita reescribir el método hashcode y hacer que el método igual y el método hashcode siempre sea lógicamente consistente.
paquete com.cxh.test1; import java.util.hashmap; import java.util.hashset; import java.util.set; Gente de clase {nombre de cadena privada; edad privada int; Public People (Nombre de cadena, int Age) {this.name = name; this.age = edad; } public void setAge (int Age) {this.age = edad; } @Override public int hashcode () {// TODO Método Generado automático STUB Return Name.HashCode ()*37+edad; } @Override public boolean iguales (object obj) {// TODO Método Generado automático Devuelve this.name.equals (((People) obj) .name) && this.age == ((personas) obj) .age; }} clase pública Main {public static void main (string [] args) {People P1 = New People ("Jack", 12); System.out.println (p1.hashcode ()); Hashmap <People, Integer> Hashmap = New Hashmap <People, Integer> (); Hashmap.put (P1, 1); System.out.println (hashmap.get (nueva gente ("Jack", 12))); }}De esta manera, el resultado de la salida será "1".
El siguiente pasaje se extrae del libro Efective Java:
Durante la ejecución del programa, siempre que la información utilizada en la operación de comparación del método igual no se haya modificado, el método hashcode debe devolver consistentemente el mismo entero.
Si los dos objetos son iguales de acuerdo con el método igual, entonces llamar al método hashcode de los dos objetos debe devolver el mismo resultado entero.
Si los dos objetos se comparan la desigualdad de acuerdo con el método igual, el método hashcode no necesariamente devuelve diferentes enteros.
Es fácil entender el segundo y tercer artículo, pero el primer artículo a menudo se ignora. También hay un pasaje similar al primero en la página P495 en el libro "Pensamiento de programación Java":
"The most important factor when designing hashCode() is: whenever you call hashCode() on the same object, the same value should be generated. If an object is added to the HashMap with put(), and another hashCode value is generated when it is taken out with get(), then the object cannot be obtained. So if your hashCode method depends on the variable data in the object, users should be careful, because when this data changes, the El método hashcode () generará un código hash diferente ".
Aquí hay un ejemplo:
paquete com.cxh.test1; import java.util.hashmap; import java.util.hashset; import java.util.set; clase de personas {nombre de cadena privada; edad privada int; Public People (Nombre de cadena, int Age) {this.name = name; this.age = edad; } public void setAge (int Age) {this.age = edad; } @Override public int hashcode () {// TODO Método Generado automático STUB Return Name.HashCode ()*37+edad; } @Override public boolean iguales (object obj) {// TODO Método Generado automático Devuelve this.name.equals (((People) obj) .name) && this.age == ((personas) obj) .age; }} clase pública Main {public static void main (string [] args) {People P1 = New People ("Jack", 12); System.out.println (p1.hashcode ()); Hashmap <People, Integer> Hashmap = New Hashmap <People, Integer> (); Hashmap.put (P1, 1); p1.setage (13); System.out.println (hashmap.get (p1)); }}El resultado de esta salida de código es "nulo", y todos deben ser claros sobre las razones.
Por lo tanto, al diseñar métodos hashcode e igual a los métodos, si los datos en el objeto son volátiles, es mejor no confiar en este campo en los métodos iguales y los métodos hashcode.
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.