Principios básicos del mapa de Goland
En términos generales, el mapa de Golang es un mapa hash, que se implementa en forma de matriz + lista vinculada, y los conflictos hash se eliminan mediante el método de cremallera.
El mapa de Golang consta de dos estructuras importantes, hmap y bmap (que se explican a continuación). El principal es que hmap contiene un puntero a la matriz bmap y la clave obtiene un número después de la función hash. Los bits bajos de este número se usan para seleccionar bmap (el puntero de la matriz bmap en la siguiente tabla), y los bits altos se usan para colocarlo en la matriz [8] uint 8 de bmap para una prueba y error rápida. Entonces un bmap puede apuntar al siguiente bmap (zip).
La implementación subyacente de map en Golang es una tabla hash, por lo que el proceso de implementación de map es en realidad el proceso de implementación de una tabla hash. En esta tabla hash, hay dos estructuras principales, una se llama hmap (el encabezado del mapa de Go) y la otra se llama bmap (el depósito del mapa de Go, generalmente llamado depósito). Las dos estructuras se muestran a continuación:
hmap:
Hay muchos campos en el gráfico, pero es fácil entender la arquitectura del mapa. Solo debe preocuparse por un campo marcado en rojo: la matriz del depósito. La estructura utilizada para el almacenamiento en los mapas de Golang es una matriz de barriles. ¿Cuál es la estructura del depósito (bmap)?
Depósito:
En comparación con hmap, la estructura del depósito es más simple. Los campos marcados en rojo siguen siendo las claves y valores del mapa. utilizamos se almacenan aquí. La matriz de "hash alto" registra el "índice" asociado con la clave en el depósito actual; hablaremos de esto más adelante. Otro campo es un puntero al depósito extendido, que forma una estructura de lista vinculada de los depósitos. Por ejemplo, como se muestra a continuación:
Se puede ver que la relación entre hmap y depósito es la siguiente:
El depósito es una lista vinculada, por lo que la estructura general debería ser así :
La característica de una tabla hash es que tiene una función hash, que realiza una operación hash sobre la clave que envías para obtener un valor único, que suele ser un valor numérico. El mapa de Golang también tiene una función hash que también calculará un valor único. Golang también usa este valor de manera muy interesante.
Golang divide el valor obtenido en dos partes según el propósito: bit alto y bit bajo.
Como se muestra en la figura, el azul es alto y el rojo es bajo. Luego, los bits bajos se usan para encontrar a qué depósito en el hmap pertenece la clave actual, y los bits altos se usan para encontrar qué clave en el depósito. Como se mencionó anteriormente, hay un campo de atributo en el depósito que es la matriz de "valor hash alto". El valor alto azul se almacena en esta matriz y se utiliza para declarar qué "claves" están en el depósito actual, lo que facilita la búsqueda. Es importante señalar que todas las claves/valores de nuestro mapa se almacenan en la misma matriz. El orden en la matriz es el siguiente:
No tiene el formato clave0/valor0/clave1/valor1. El beneficio de esto es que elimina el espacio desperdiciado causado por el relleno cuando las claves y los valores tienen diferentes longitudes.
Ahora, podemos obtener el diagrama de estructura completo del gráfico del lenguaje Go: (La parte de orden inferior del resultado hash se usa para seleccionar qué bmap colocar el KV en la matriz de bmap, y la parte alta -La parte del pedido se utiliza para obtener una vista previa rápida de las claves y prueba y error rápidas).
Extensión del mapa
Cuando la tabla hash anterior crezca, el lenguaje Go duplicará el número de matrices de depósitos, generará una nueva matriz de depósitos y reemplazará los datos de la matriz anterior. con Mover a una nueva matriz.
Factor de carga
La condición para juzgar la expansión es el factor de carga en la tabla hash.
El factor de carga es un umbral, generalmente expresado como: el número de elementos contenidos en el hash dividido por el número total de posiciones. Es un equilibrio y un compromiso entre "oportunidades de conflicto" y "utilización del espacio": cuanto menor sea el coeficiente de carga, mayor será la tasa de espacio vacante y menor la utilización del espacio, pero cuanto mayor sea el coeficiente de carga, mayor será la utilización del espacio, pero "Las posibilidades de conflicto" también son mayores.
Cada tabla hash tiene un factor de carga, si el valor excede el factor de carga, la tabla hash se expandirá.
La fórmula para el factor de carga del mapeo de Golang es: longitud del mapeo/2^B (esta es la longitud de la matriz bmap, b es el número de bits de orden inferior) y el umbral es 6,5 . Donde b puede entenderse como el número de ampliaciones de capacidad.
Cuando la longitud del mapeo de Go crece más que la longitud del mapeo requerida por el factor de carga, el lenguaje Go generará una nueva matriz de depósitos y luego moverá la matriz de depósitos anterior al campo de atributo oldbucket. Nota: Los elementos de la matriz anterior no se escapan inmediatamente al nuevo depósito, pero los datos del depósito solo se transfieren al nuevo depósito cuando se accede a un depósito específico.
Como se muestra en la siguiente figura: durante la expansión, los datos antiguos y las matrices recién generadas se almacenarán en la estructura del mapa de Go.
La parte superior representa el depósito antiguo con datos y la parte inferior representa el depósito nuevo recién generado. El azul representa depósitos con datos, el naranja representa depósitos vacíos.
Map no migrará datos nuevos inmediatamente al expandirse, pero migrará datos antiguos cuando acceda a los datos en el depósito anterior, como se muestra en la siguiente figura:
Nota: El depósito anterior No se eliminará directamente, pero se eliminará la referencia original y GC borrará la memoria.
Eliminar datos en un mapa
Si comprende la estructura general de un mapa, los pasos básicos para buscar, actualizar y eliminar deben ser claros. No entraré en detalles aquí.
Vale la pena señalar que después de encontrar los datos en el mapa, realice las siguientes operaciones en la clave y el valor respectivamente:
1
2
Tres
Cuatro
1. Si "clave" es un tipo de puntero, déjelo en blanco y espere a que el GC lo borre;
2. Si es tipo valor, borre la memoria asociada.
3. De la misma manera, realice la misma operación en "valor".
4. Finalmente, establezca el índice de la matriz correspondiente al valor alto de la clave en vacío.