(Reimpreso) Por qué X% length == X & (length-1) in HashMap (% restante y problema de operación y conversión)

Reimpreso: Enlace original https://blog.csdn.net/ricardo18/article/details/108846384
Declaración: Si violé los derechos de alguien, comuníquese conmigo y lo eliminaré.
Bienvenidos expertos a rociarme

Uno, lleva al problema

Al explicar la implementación del código fuente de HashMap, existen los siguientes puntos:

① La capacidad inicial es 1 << 4, es decir, 24 = 16
Inserte la descripción de la imagen aquí
② El factor de carga es 0,75. Cuando la proporción de elementos almacenados en el HashMap supere el 75% de la capacidad total, amplíe la capacidad, y cuando no supere el rango del tipo int, Realice la expansión de la potencia de 2 (refiriéndose a la longitud del 2 veces original) y duplíquela
Inserte la descripción de la imagen aquí
.
Inserte la descripción de la imagen aquí
③ Cuando se agrega un nuevo elemento, se calcula la posición de este elemento en el HashMap, lo que es la operación hash del personaje principal de este artículo. Se divide en tres pasos:

El primer paso: toma el valor hashCode: key.hashCode ()

Paso 2: Participar en operaciones de alto nivel: h >>> 16

El tercer paso: operación de módulo: (n-1) y hash

static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
 
    tab[i = (n - 1) & hash];

PD: Yo mismo agregué la sexta línea de código.

Sabemos que un buen algoritmo hash puede hacer que la distribución de elementos sea más uniforme, reduciendo así las colisiones hash. El procesamiento de HashMap en esta área es muy inteligente:

El primer paso es obtener el hashCode. Este método es un método nativo decorado con nativo. Devuelve un valor de tipo int (un valor convertido según la dirección de memoria). Normalmente reescribiremos este método.

En el segundo paso, el valor hash obtenido no está firmado a la derecha en 16 bits y los bits altos se rellenan con 0. Y realice una operación XOR bit a bit con el código hash obtenido en el paso anterior. ¿Para qué sirve esto? En realidad, se trata de una función de perturbación para reducir la colisión del código hash. El desplazamiento a la derecha es de 16 bits, que es exactamente la mitad del bit de 32. El área de la mitad alta y el área de la mitad baja se XOR para mezclar los bits altos y bajos del código hash original para aumentar la aleatoriedad de los bits bajos. Además, las funciones mixtas de bajo nivel se dopan con algunas de las funciones de alto nivel, de modo que la información de alto nivel también se conserva disfrazada. Eso es para garantizar que tanto los bits de bits altos como los bajos estén involucrados en el cálculo de Hash.

Si está interesado, puede echar un vistazo a JDK1.7. De hecho, hizo 4 perturbaciones, y solo lo hizo una vez en JDK1.8. Supongo que es para reducir los conflictos y garantizar la eficiencia.
  Inserte la descripción de la imagen aquí
El enfoque de este artículo es el tercer paso, el valor hash obtenido a través de los dos pasos anteriores y la longitud de colección del HashMap menos 1 para la operación AND bit a bit: (n-1) & hash. Pero, de hecho, muchos algoritmos hash, para uniformar la distribución de elementos, utilizan una operación de módulo, utilizando un valor para modular la longitud total, es decir, n% hash. Sabemos que la eficiencia de & en la computadora es mucho mayor que%, entonces, ¿cómo convertir% en & operación? En HashMap, (n-1) & hash se usa para el cálculo, entonces, ¿por qué es esto?

Esta es la pregunta que entenderemos en este blog.

2. Conclusión

Primero damos la conclusión:

当 lenth = 2n 时 , X% length = X & (longitud - 1)

En otras palabras, cuando la longitud es 2 elevado a la n-ésima potencia, la operación de módulo% se puede transformar en una operación AND bit a bit.

Por ejemplo: 9% 4 = 1, 9 es 1001 en binario, 4-1 = 3 y 3 es 0011 en binario. 9 y 3 = 1001 y 0011 = 0001 = 1

Otro ejemplo: 12% 8 = 4, el valor binario de 12 es 1100 y el valor binario de 8-1 = 7, 7 es 0111. 12 & 7 = 1100 & 0111 = 0100 = 4

Los dos ejemplos 4 y 8 anteriores son ambos la n-ésima potencia de 2, y la conclusión es verdadera ¿Qué pasa cuando la longitud no es la n-ésima potencia de 2?

Por ejemplo: 9% 5 = 4, 9 es 1001 en binario, 5-1 = 4, 4 es 0100. 9 & 4 = 1001 & 0100 = 0000 = 0. Evidentemente no es cierto.

¿Por qué es como este? Analicemos en detalle a continuación.

3. Proceso de análisis

Primero que nada, necesitamos conocer las siguientes reglas:

①, "<<" Desplazamiento a la izquierda: agregue 0 al bit vacante de la derecha, y el bit de la izquierda se exprimirá desde el principio de la palabra, y el desplazamiento a la izquierda por un bit equivale a multiplicar por 2.

②, ">>" desplazamiento a la derecha: el bit de la derecha se elimina, el valor de desplazamiento a la derecha por un bit es equivalente a dividir por 2. Para el espacio desplazado a la izquierda, si es un número positivo, el espacio se rellena con 0, si es un número negativo, se puede rellenar con 0 o 1, según el sistema informático utilizado.

③, ">>>" desplazamiento sin signo hacia la derecha, el bit de la derecha se exprime y se agrega 0 al espacio desplazado hacia la izquierda.

Según las características de los números binarios, creo que todo el mundo lo entiende bien.

Dado un número decimal arbitrario XnXn-1Xn-2 ... X1X0, lo descomponemos en representación binaria:

XnXn-1Xn-2… X1X0 = Xn 2n + Xn-1 2n-1 +… + X1 21 + X0 20 3-1 公式

El número decimal aquí tiene solo tres dígitos. De manera similar, cuando hay N dígitos, la potencia de 2 aumentará de 0 a N sucesivamente.

Volviendo a la conclusión anterior: cuando lenth = 2n, X% length = X & (length-1)

Y para la división, el dividendo cumple con la tasa de distribución (el divisor no cumple):

Establecido: (a + b) ÷ c = a ÷ c + b ÷ c fórmula 3-2

No es cierto: a ÷ (b + c) ≠ a ÷ c + b ÷ c

A través de la fórmula 3-1 y la fórmula 3-2, podemos conseguir que cuando cualquier número decimal se divide por un número de 2k, podemos convertir el número decimal en la representación de la fórmula 3-1:

(XnXn-1Xn-2… X1X0) / 2k = (Xn 2n + Xn-1 2n-1 +… + X1 21 + X0 20) / 2k = Xn 2n / 2k + Xn-1 2n-1 / 2k +… + X1 21 / 2k + X0 20 / 2k

Si queremos encontrar el resto de la fórmula anterior, creo que puede decirlo de un vistazo:

①. Cuando 0 <= k <= n, el resto es Xk 2k + Xk-1 2k-1 +… + X1 21 + X0 20, es decir, la n-ésima potencia mayor que k, descartamos (large Todos ellos pueden ser divisibles por 2k), y todos nos quedamos atrás (más pequeño que k no puede ser divisible por 2k). Entonces el resto es el resto.

②. Cuando k> n, el resto es el número decimal completo.

Viendo esto, estamos muy cerca de probar la conclusión. Volviendo a la operación de desplazamiento binario mencionada anteriormente, desplazarse hacia la derecha en n bits significa dividir por la potencia de 2n. De esto obtenemos una conclusión muy importante:

Un número decimal toma el resto de un número 2n. Podemos convertir este decimal en un número binario y desplazar el número binario a la derecha n lugares. Los n dígitos eliminados son el resto.

Sepa cómo calcular el resto, entonces, ¿cómo sacamos el número de n?

Veamos 20, 21, 22 ... 2n en binario de la siguiente manera:

0001 ,0010 ,0100 ,1000 ,10000…

Reducimos el número anterior en uno:

0000 ,0001 ,0011,0111,01111…

De acuerdo con la regla del operador AND &, cuando todos los bits son 1, el resultado es 1, de lo contrario es 0. Entonces, cuando cualquier número binario toma el resto de 2k, podemos realizar una operación AND bit a bit entre este número binario y (2k-1), incluso si se retiene el resto.

Esto prueba perfectamente la conclusión dada anteriormente:

当 lenth = 2n 时 , X% length = X & (longitud - 1)

Tenga en cuenta que debe ser 2n para satisfacer la fórmula anterior; de lo contrario, es incorrecto.

para resumir

A través del proceso de análisis anterior, probamos perfectamente la exactitud de la fórmula. Volviendo al proceso de implementación de HashMap, sabemos por qué la capacidad inicial de HashMap es 1 << 4, y cada expansión se duplica. Porque el algoritmo hash debe quedar perfectamente satisfecho.

Supongo que te gusta

Origin blog.csdn.net/qq_45531729/article/details/112370306
Recomendado
Clasificación