El método igual y el método hashcode en Java están en objeto, por lo que cada objeto tiene estos dos métodos. A veces necesitamos implementar necesidades específicas y podemos reescribir estos dos métodos. Hoy, presentaremos las funciones de estos dos métodos.
Los métodos Equals () y HashCode () se utilizan para compararse en la misma clase, especialmente cuando almacenan el mismo objeto de clase en el contenedor, como SET para almacenar objetos en la misma clase.
Aquí primero tenemos que entender un problema:
Dos objetos con igual () iguales, hashcode () deben ser iguales, y dos objetos con igual () no iguales, no pueden probar que su hashcode () no es igual. En otras palabras, para dos objetos cuyo método igual () no es igual, hashcode () puede ser igual. (Mi comprensión es causado por los conflictos del código hash cuando se genera)
Aquí hashcode es como el índice de cada personaje en el diccionario, y igual () es como comparar diferentes palabras bajo el mismo carácter en el diccionario. Al igual que en el diccionario, la búsqueda de las dos palabras "yo" y "espontáneas" bajo la palabra "yo" en el diccionario, si es igual () para determinar la igualdad de la consulta de palabras, es la misma palabra. Por ejemplo, las dos palabras en comparación con igual () son "self", entonces los valores obtenidos por el método hashcode () deben ser iguales en este momento; Si el método igual () compara las palabras "self" y "espontánea", entonces el resultado es que no desea esperar, pero ambas palabras pertenecen a las palabras "yo" y, al buscar índices, es decir, hashcode () es el mismo. Si iguales () compara las palabras "self" y "ellos", entonces los resultados también son diferentes, y los resultados obtenidos por hashcode () también son diferentes en este momento.
Por el contrario: hashcode () es diferente, y es igual () se puede introducir; hashcode () es igual, igual () puede ser igual o no ser igual. En la clase de objeto, el método hashcode () es un método local, que devuelve el valor de dirección del objeto. El método igual () en la clase de objeto también compara los valores de dirección de los dos objetos. Si igual () es igual, significa que los valores de dirección de los dos objetos también son iguales, por supuesto, hashcode () es igual;
Al mismo tiempo, el algoritmo hash proporciona una alta eficiencia para encontrar elementos
Si desea averiguar si un objeto está contenido en una colección, ¿cómo escribe el código de programa aproximado?
Por lo general, elimina cada elemento uno por uno para comparar con el objeto que está buscando. Cuando encuentre que el resultado de la comparación de métodos iguales entre un elemento y el objeto que está buscando, deje de buscar y devuelva información positiva. De lo contrario, devuelva información negativa. Si hay muchos elementos en una colección, como 10,000 elementos y no contienen el objeto que está buscando, significa que su programa necesita eliminar 10,000 elementos de la colección y comparar uno por uno para llegar a una conclusión.
Alguien ha inventado un algoritmo hash para mejorar la eficiencia de encontrar elementos de un conjunto. De esta manera, el set se divide en varias áreas de almacenamiento. Cada objeto puede calcular un código hash, y el código hash se puede agrupar (calculado usando diferentes funciones hash). Cada grupo corresponde a un área de almacenamiento determinada. Según el hash de un objeto, puede determinar en qué área se debe almacenar el objeto. Hashset utiliza un algoritmo hash para acceder al conjunto de objetos. Utiliza internamente el método para tomar el resto de un cierto número N (esta función hash es la más fácil) de agrupar y dividir los códigos hash. La clase de objeto define un método hashcode () para devolver el código hash de cada objeto Java. Al buscar un objeto de una colección hashset, el sistema Java primero llama al método hashcode () del objeto para obtener la tabla de código hash del objeto. , luego encuentre el área de almacenamiento correspondiente basada en el hash, y finalmente obtenga cada elemento en el área de almacenamiento y compárelo con el objeto para el método igual; De esta manera, puede obtener la conclusión sin atravesar todos los elementos de la colección. Se puede ver que la colección Hashset tiene un buen rendimiento de recuperación de objetos, pero la eficiencia de almacenar objetos en la colección hashset es relativamente baja, porque al agregar un objeto a la colección hashset, primero debe calcular el código hash del objeto y determinar la ubicación de almacenamiento del objeto en la colección basada en este código hash. Para garantizar que los objetos de instancia de una clase se puedan almacenar normalmente en hashset, los resultados de los dos objetos de instancia de esta clase también deben ser iguales cuando los resultados comparados por el método igual () son iguales; es decir, si el resultado de OBJ1.Equals (OBJ2) es verdadero, entonces el resultado de la siguiente expresión también debe ser verdadero:
obj1.hashcode () == obj2.hashcode ()
En otras palabras: cuando reescribimos el método igual de un objeto, debemos reescribir su método hashcode. Sin embargo, si no reescribimos su método hashcode, el método hashcode en el objeto objeto siempre devuelve la dirección hash de un objeto, y esta dirección nunca es igual. Entonces, incluso si el método igual se reescribe en este momento, no habrá ningún efecto específico, porque si el método hashcode no quiere esperar, no llamará al método igual para la comparación, por lo que no tiene sentido.
Si el método hashcode () de una clase no sigue los requisitos anteriores, entonces cuando los resultados de la comparación entre los dos objetos de instancia de esta clase son iguales con el método igual (), no deben almacenarse en el conjunto establecido al mismo tiempo. Sin embargo, si se almacenan en el conjunto de hashset, dado que el valor de retorno de su método hashcode () es diferente (el valor de retorno del método hashcode en el objeto siempre es diferente), el segundo objeto se puede colocar en un área diferente desde el primer objeto de acuerdo con el cálculo del código hash, por lo que no puede comparar el método iguales con el primer objeto, se puede corregir en la colección de hahset. El método hashcode () en la clase de objeto no puede cumplir con los requisitos del objeto que se almacena en el hetero hash, porque su valor de retorno se calcula a partir de la dirección de memoria del objeto. El valor hash devuelto por el mismo objeto en cualquier momento durante la ejecución del programa siempre no ha cambiado. Por lo tanto, siempre que sea dos objetos de instancia diferentes, incluso si los resultados de comparación de su método igual son iguales, el valor de retorno de su método hashcode predeterminado es diferente.
Echemos un vistazo a un ejemplo específico:
Objeto rectobject: paquete com.weijia.demo; public class rectobject {public int x; público int y; public rectobject (int x, int y) {this.x = x; this.y = y; } @Override public int hashcode () {final int prime = 31; int resultado = 1; resultado = prime * resultado + x; resultado = prime * resultado + y; resultado de retorno; } @Override public boolean iguales (objeto obj) {if (this == obj) return true; if (obj == null) return false; if (getClass ()! = obj.getClass ()) return false; final rectobject otro = (rectobject) obj; if (x! = other.x) {return false; } if (y! = other.y) {return false; } return verdadero; }} Anulamos los métodos hashcode e equivalimos a los métodos de clase principal y vemos que en el húsico e iguala los métodos, si los valores x e y de los dos objetos rectobject son iguales, sus valores de hashcode son iguales y equivalen a devolverlo a verdad;
Aquí está el código de prueba:
paquete com.weijia.demo; import java.util.hashset; Public Class Demo {public static void main (string [] args) {Hashset <RectObject> set = new Hashset <RectObject> (); Rectobject r1 = nuevo rectobject (3,3); Rectobject r2 = nuevo rectobject (5,5); Rectobject R3 = nuevo rectobject (3,3); set.add (r1); set.add (r2); set.add (r3); set.add (r1); System.out.println ("size:"+set.size ()); }} Almacenamos cuatro objetos en el hashset e imprimimos el tamaño de la colección de conjuntos. ¿Cuál es el resultado?
Resultado de ejecución: tamaño: 2
¿Por qué es 2? Esto es muy simple, porque reescribimos el método hashcode de la clase Rectobject. Mientras los valores de atributo X e Y del objeto Rectobject sean iguales, entonces sus valores de hashcode también son iguales. Entonces, primero compare los valores de hashcode. Los valores de atributo X e Y de los objetos R1 y R2 no son iguales, por lo que sus hashcodes son diferentes, por lo que el objeto R2 se puede colocar, pero los valores de atributo X e Y del objeto R3 son los mismos que los valores de atributo del objeto R1, por lo que el código hash es igual. En este momento, comparamos el método igual de R1 y R3, porque los valores x e y de los dos son iguales, por lo que los objetos R1 y R3 son iguales, por lo que R3 no se puede colocar. Además, agregar un R1 al final no se agrega, por lo que solo hay dos objetos, R1 y R2 en el conjunto establecido.
A continuación, comentamos el método hashcode en el objeto rectobject, es decir, no anulamos el método hashcode en el objeto objeto y ejecutamos el código:
Resultado de ejecución: tamaño: 3
Este resultado también es muy simple. Primero, juzgue el húsico del objeto R1 y el objeto R2. Debido a que el método hashcode en el objeto devuelve el resultado de conversión de la dirección de memoria local del objeto, el cho de diferentes objetos de instancia es diferente. Del mismo modo, debido a que el hashcode de R3 y R1 también es desigual, pero R1 == R1, por lo que en el conjunto final, solo hay tres objetos R1, R2 y R3, por lo que el tamaño es 3
A continuación, comentamos el contenido del método igual en el objeto rectobject, devuelve directamente falso, sin comentar el método hashcode, y ejecutar el código:
Resultado de ejecución: tamaño: 3
Este resultado es un poco inesperado, analicémoslo:
Primero, los objetos de R1 y R2 comparan hashcode, que no son iguales, por lo que R2 se pone en establecido, y luego miran los métodos de húsico de R3 y comparan R1 y R3, que son iguales, y luego comparan sus métodos iguales. Debido a que el método igual siempre devuelve falsos, R1 y R3 también son desiguales, y no hay necesidad de mencionar R3 y R2. Los hashcodes de sus dos no son iguales, por lo que R3 se coloca en el conjunto y luego mira R4 y comparan R1 y R4 encuentran que los hashcodes son iguales. Al comparar el método igual, debido a que los devoluciones iguales, R1 y R4 no son iguales, los mismos R2 y R4 también son desiguales, y R3 y R4 también son desiguales, por lo que R4 se puede colocar en el conjunto establecido, por lo que el resultado debe ser el tamaño: 4, entonces, ¿por qué es 3?
En este momento, necesitamos verificar el código fuente de hashset. Aquí está el código fuente del método Agregar en hashset:
/*** Agrega el elemento especificado a este conjunto si aún no está presente. * Más formalmente, agrega el elemento especificado <tt> e </tt> a este conjunto si * este conjunto no contiene elemento <tt> e2 </tt> tales que * <tt> (e == null? E2 == null: e.equals (e2)) </tt>. * Si este conjunto ya contiene el elemento, la llamada deja el conjunto * sin cambios y devuelve <tt> false </tt>. * * @param e elemento que se agregará a este conjunto * @return <tt> true </tt> Si este conjunto aún no contenía el elemento */ public boolean public (e e) {return map.put (e presente) == null; } Aquí podemos ver que Hashset se implementa en base a hashmap. Estamos haciendo clic en el método Put de HashMap, el código fuente es el siguiente:
/*** Asocia el valor especificado con la clave especificada en este mapa. * Si el mapa contenía previamente una asignación para la clave, se reemplaza el valor anterior *. * * @param clave clave con la que el valor especificado se asociará * @param valor del valor para estar asociado con la clave especificada * @return el valor anterior asociado con <tt> clave </tt>, o * <tt> null </tt> si no había asignación para <tt> key </tt>. * (A <tt> null </tt> return también puede indicar que el mapa * previamente asociado <tt> null </tt> con <tt> key </tt>.) */Public v put (k key, valor v) {if (key ==) return PutfornullKey (valor); int hash = hash (clave); 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; } Veamos principalmente las condiciones del juicio de IF.
Primero, determinamos si el húsico es igual. Si no es igual, omita directamente. Si es igual, compare si estos dos objetos son iguales o el método igual de estos dos objetos. Debido a que se realiza u opera, siempre que uno sea cierto, podemos explicarlo aquí. De hecho, el tamaño del conjunto anterior es 3, porque el último R1 no se puso, y se pensó que R1 == R1 devolvió verdadero, por lo que no se puso. Por lo tanto, el tamaño del conjunto es 3. Si establecemos el método hashcode para que siempre devuelva falso, este conjunto será 4.
Finalmente, echemos un vistazo a la fuga de memoria causada por hashcode: mire el código:
paquete com.weijia.demo; import java.util.hashset; Public Class Demo {public static void main (string [] args) {Hashset <RectObject> set = new Hashset <RectObject> (); Rectobject r1 = nuevo rectobject (3,3); Rectobject r2 = nuevo rectobject (5,5); Rectobject R3 = nuevo rectobject (3,3); set.add (r1); set.add (r2); set.add (r3); r3.y = 7; System.out.println ("tamaño antes de la eliminación:"+set.size ()); set.remove (R3); System.out.println ("tamaño después de la eliminación:"+set.size ()); }} Resultados de ejecución:
Tamaño antes de la eliminación: 3
Tamaño eliminado: 3
Rush, encontré un problema, y fue un gran problema. Llamamos a eliminar para eliminar el objeto R3, pensando que se había eliminado, pero de hecho no fue eliminado. Esto se llama fuga de memoria, que es un objeto no utilizado, pero todavía está en la memoria. Entonces, después de operar esto muchas veces, la memoria explota. Eche un vistazo al código fuente de eliminación:
/*** Elimina el elemento especificado de este conjunto si está presente. * Más formalmente, elimina un elemento <tt> e </tt> tal que * <tt> (o == null? E == null: o.equals (e)) </tt>, * si este conjunto contiene dicho elemento. Devuelve <Tt> True </tt> if * Este conjunto contenía el elemento (o de manera equivalente, si este conjunto * cambió como resultado de la llamada). (Este conjunto no contendrá el * elemento * una vez que regrese la llamada. }
Luego, mire el código fuente del método eliminar:
/*** Elimina la asignación de la clave especificada de este mapa si está presente. * * @param clave clave cuya asignación se eliminará del mapa * @return El valor anterior asociado con <tt> clave </tt>, o * <tt> null </tt> si no hubo asignación para <tt> key </tt>. * (A <tt> null </tt> return también puede indicar que el mapa * anteriormente asociado <tt> null </tt> con <tt> key </tt>.) */Public v remove (tecla de objeto) {Entry <k, v> e = removeStryForKey (key); return (e == null? null: e.value); } Echemos un vistazo al código fuente del método RemoughentryForKey:
/** * Elimina y devuelve la entrada asociada con la clave especificada * en el hashmap. Devuelve nulo si el hashmap no contiene mapeo * para esta clave. */ Entrada final <k, v> removeTryForKey (clave de objeto) {int hash = (key == null)? 0: hash (clave); int i = indexfor (hash, table.length); Entrada <k, v> prev = tabla [i]; Entrada <k, v> e = anterior; while (e! = null) {entry <k, v> next = e.next; Objeto k; if (e.hash == hash && ((k = e.key) == key || (key! = null && key.equals (k)))) {modCount ++; tamaño--; if (prev == e) tabla [i] = next; else Prev.next = Next; E.Recordremoval (esto); regresar e; } prev = e; e = siguiente; } return e; } Vemos que al llamar al método de eliminación, primero usaremos el valor hashcode del objeto para encontrar el objeto y luego eliminarlo. Este problema se debe a que hemos modificado el valor del atributo Y del objeto R3. Y debido a que el método hashcode del objeto Rectobject tiene un valor Y participando en la operación, el Ccode del objeto R3 ha cambiado, por lo que R3 no se encuentra en el método de eliminación, por lo que la eliminación falló. Es decir, el hashcode de R3 ha cambiado, pero la ubicación que almacena no se ha actualizado y todavía está en su ubicación original, por lo que cuando usamos su nuevo hashcode para encontrarlo, definitivamente no lo encontraremos.
De hecho, el método anterior es muy simple de implementar: como se muestra en la figura:
Una tabla hash lineal muy simple, la función hash utilizada es mod, el código fuente es el siguiente:
/*** Devuelve el índice para el código hash h. */ static int indexFor (int h, int longitud) {return h & (longitud-1); } Esta es en realidad una operación de mod, pero este tipo de operación es más eficiente que el % de operación.
1,2,3,4,5 significa el resultado de MOD, y cada elemento corresponde a una estructura de lista vinculada. Por lo tanto, si desea eliminar una entrada <k, v>, primero obtendrá hashcode, para obtener el nodo de encabezado de la lista vinculada y luego iterar a través de la lista vinculada. Si hashcode y iguales son iguales, elimine este elemento.
La filtración de memoria anterior me dice un mensaje: si participamos en la operación hashcode del valor del atributo del objeto, no podemos modificar su valor de atributo al eliminarlo, de lo contrario se producirán problemas graves.
De hecho, también podemos mirar el método hashcode e igual al método de los tipos de objetos correspondientes a los 8 tipos de datos básicos.
Entre ellos, el Código hash del tipo básico en 8 es muy simple para devolver directamente su tamaño numérico. El objeto de cadena es a través de un método de cálculo complejo, pero este método de cálculo puede garantizar que si los valores de esta cadena sean iguales, su húsico será igual. Los 8 tipos básicos de métodos iguales son comparar directamente los valores numéricos, y el tipo de tipo de cadena es igual a los valores de las cadenas.
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.