¡Java entra en vigencia el martes! Cumplir con el contrato `hashCode`

El tema de hoy coincide con el tema de la semana pasada. Esta semana estamos hablando de la función hashCode. Al igual que la función que discutimos la semana pasada, este método también tiene un contrato, aunque es relativamente simple, debe seguirse. Al igual que la firma, no toma parámetros y devuelve un número entero. Entonces, firmemos el contrato:

  • Si el objeto no se ha cambiado, el valor devuelto por la función debe permanecer sin cambios. Si dos objetos son realmente iguales, sus códigos hash deben ser iguales. (Esto no es un requisito). Dado que dos objetos en realidad no son iguales, no es necesario tener valores de código hash diferentes.

De esta manera, puede ver que el código hash está estrechamente relacionado con la igualdad. El segundo requisito es el requisito más roto. Si no implementa una función de código hash, dos objetos efectivamente equivalentes pueden no tener el mismo valor devuelto por la función. Así que veamos un ejemplo:

Map<Address, String> addressBook = new HashMap<>();
addressBook.put(new Address("123","Foggy Lane" "Made Up City", "USA"), "James");
addressBook.get(new Address("123","Foggy Lane" "Made Up City", "USA"));

Dado el ejemplo anterior, se espera que la tercera línea devuelva "James", pero si la función de código hash se escribe incorrectamente, devolverá un valor nulo.

Por lo tanto, escribamos la función de código hash legal más simple:

@Override
public int hashCode() {
  return 42;
}

Sí, de acuerdo con el contrato, esta es una función de código hash totalmente efectiva. Si se llama varias veces en el mismo objeto, siempre devuelve lo mismo; para objetos funcionalmente equivalentes, devuelve el mismo valor; para dos objetos funcionalmente equivalentes, devuelve el mismo código hash. . Sin embargo, incluso si cumple con el contrato, el valor devuelto no cambia, lo cual es una idea terrible y puede causar una degradación significativa del rendimiento. Por ejemplo, en términos de rendimiento, la función de código hash anterior convertirá efectivamente HashMap en una lista vinculada.

Debe haber una mejor manera, y existe. Java efectivo nos proporciona una forma de crear funciones sólidas de código hash.

  1. Declare el resultado del nombre entero e inicialícelo al valor del primer campo válido del objeto (el recordatorio "campo válido" se refiere al campo que participa en la decisión cuando dos objetos son iguales), tal como se calcula en el paso 2. Para cada campo significativo restante, haga lo siguiente para calcular un código hash para este campo entero: si el campo es el campo original, use la versión en recuadro del campo para calcular la función hashCode. Por ejemplo: Double.hashCode (valor) Si el campo es una referencia de objeto, llame al entero o la función hashCode de ese objeto. Para nulo, use el valor 0 si es una matriz, luego trate cada elemento importante como un campo separado o use Arrays.hashCode si todos son importantes. Combine el código hash que acaba de calcular con el resultado de la siguiente manera: resultado = 31 * resultado + nuevo resultado de retorno de código de campo

Si sigue correctamente el algoritmo anterior, debe calcular un código hash confiable. La ventaja de este algoritmo es que el orden de las operaciones es importante, por lo que se puede lograr una mejor asignación. Multiplicar por 31 es bueno porque es un primo impar. Los números impares ayudan al desbordamiento de enteros, mientras que los números primos son solo porque los números primos son geniales. En realidad, parece que se ha convertido en un estándar. Echemos un vistazo a nuestra nueva función hashCode de dirección.

@Override
public int hashCode() {
  int result = streetAddress.hashCode();
  result = 31 * result + road.hashCode();
  result = 31 * result + country.hashCode();
  return result;
}

Bastante simple, pero efectivo. ¿Hay una manera más fácil de escribir estas funciones? Por supuesto Un ejemplo es el uso de Objects.hashCode (significativamenteField1, identificaciónField2, ...). Muy bien, porque es una función hashCode de una línea. La desventaja es que su rendimiento es peor que el ejemplo anterior. Pero creo que la mejor manera es usar algo como Lombok. Lombok implementó la anotación @EqualsAndHashCode a través de ella. Los mantenedores de Lombok están muy contentos de forzar igual a hashCode.

Finalmente, analicemos algunos códigos hash para recordar:

  • Siempre sobrescriba el código hash cuando sobrescriba igual para incluir todos los valores en uso iguales al código hash como parte del cálculo. No comparta fuera de la función cómo se calcula el código hash, innecesariamente puede vincularlo a una implementación deficiente.

de: https://dev.to//kylec32/effective-java-tuesday-obey-the-hashcode-contract-3onl

Publicado 0 artículos originales · me gusta 0 · visitas 634

Supongo que te gusta

Origin blog.csdn.net/cunxiedian8614/article/details/105691244
Recomendado
Clasificación