Explicación detallada del principio de implementación subyacente de HashMap, ensayo estereotipado JAVA

El principio de implementación subyacente:

HashMapLa estructura de datos subyacente Java8estaba en la forma anterior 数组+链表, y tiene la forma de matriz + lista enlazada + árbol rojo-negro después de java8;

Cuando se crea, la longitud predeterminada es 0, y la longitud se expandirá por primera vez cuando se llame al método put. La longitud será 16. Cuando el factor de carga en nosotros exceda el factor de carga predeterminado de 0.75, la capacidad 元素个数/总容量será ampliado, y la capacidad se ampliará 2 veces cada vez;

Al agregar un objeto, primero calculará la clave hashCode()y luego llamará al HashMapmétodo hash()para el hash secundario. Finalmente, calcule el subíndice del cubo tomando la longitud de la matriz del módulo. 在jdk1.8后优化了取模计算方式Si el contenido del subíndice actual está vacío, agréguelo directamente. Si no está vacío Luego juzgue si la clave hashCode()es consistente, si no, busque hacia atrás hasta que el contenido del subíndice esté vacío, agréguelo jdk1.8是尾部追加,jdk1.8之前是头部追加, si es consistente, realice equalscálculos para determinar si el contenido es el mismo, si es el mismo, reemplace el valor, y si es diferente, continúe comparando con el siguiente contenido;

Después de la adición, cuando la longitud de la lista enlazada actual sea mayor que 8 y la capacidad de la matriz sea mayor o igual a 64, la lista enlazada se convertirá en un árbol rojo-negro. La matriz es mayor que la cantidad de elementos en la matriz una vez que se completa la adición o se convierte en un árbol rojo-negro, se llamará para expandirse y se descargarán todos los depósitos. La marca se vuelve 数组长度*负载因子a resize()calcular 重新取模当前新的容量计算桶下标.

Cuestión de elevación:

estructura de datos subyacente

  1. La estructura de datos subyacente antes de java8 es matriz + lista vinculada
  2. La estructura de datos subyacente después de java8 es matriz + lista enlazada + árbol rojo-negro

¿Por qué usar un árbol rojo-negro?

Porque si no usa un árbol rojo-negro, si desea acceder a un dato, si la lista enlazada es muy larga, tenemos que compararla desde el principio, lo que afectará el rendimiento de HashMap 时间复杂度为O(n). un árbol rojo-negro, se asignará de acuerdo con el tamaño (el que es más grande que el nodo principal se coloca a la derecha, el más pequeño que el nodo principal se coloca a la izquierda), solo necesitamos comparar el tamaño a través del código hash (si el código hash es el mismo, compare el valor de la cadena) para omitir comparaciones redundantes y ubicar el elemento que necesitamos encontrar 时间复杂度为O(log₂ⁿ).

¿Por qué no convertirlo en un árbol rojo-negro cuando brota?

Porque al principio, cuando la longitud de la lista enlazada es corta, su rendimiento es mejor que el del árbol rojo-negro. Solo cuando la lista enlazada es larga, el rendimiento no es tan bueno como el del árbol rojo-negro. .
Y la estructura de datos de la lista enlazada es Node, y la estructura de datos del árbol rojo-negro es TreeNode.Las variables miembro en TreeNode son mucho más que las de la lista enlazada, por lo que la memoria ocupada por el árbol rojo-negro es también más que el de la lista enlazada.

¿Por qué el umbral de treeing es 8?

Normalmente, si el hash es lo suficientemente aleatorio y el factor de carga es 0,75, la longitud de la lista enlazada en HashMap rara vez supera los 8. Elegir 8 hace que la conversión en un árbol rojo-negro sea menos probable. El árbol rojo-negro debe convertirse en Para evitar ataques DoS y evitar la degradación del rendimiento cuando la lista vinculada es demasiado larga, la creación de árboles debe ser accidental.

¿Cuándo se convertirá en una lista enlazada?

Cuando la longitud es menor o igual a 6, se convertirá en un
removenodo de árbol rojo-negro de lista enlazada.Si uno de root, root.left, root.right, root.left.left es nulo, también será convertido en una lista enlazada root表示根节点,left表示左边的节点,right表示右边的节点.

¿Cómo se calcula el subíndice del índice?
Antes de 1.8: el valor hash secundario toma la longitud de la matriz de módulo.
1.8: Calcule el hashCode() del objeto, luego llame al método hash() de HashMap para el hash secundario y finalmente & 位运算(capacidad 当前容量- 1) para obtener el índice.

¿Por qué hash secundario?

Si los valores de orden inferior del HashCode son los mismos, se producirá el problema de la distribución desigual del hash si no se realiza el segundo hash.Para garantizar que los datos se distribuyan uniformemente en la matriz y evitar la situación en la que el la lista enlazada es demasiado larga, necesitamos realizar una segunda operación hash.

¿Por qué la capacidad del arreglo es 2 a la n-ésima potencia?

Al calcular el índice, si es la enésima potencia de 2, puede usar la operación bit-AND en lugar del módulo, que es más eficiente; al expandir, use el segundo hash & oldCap ==0 El elemento permanece en el 旧的容量original posición, de lo contrario se mueve a la nueva posición 旧位置+oldCap.

¿Qué sucede si la capacidad de la matriz no es la potencia de 2?

Todo lo mencionado anteriormente es para el método de optimización cuando la capacidad es la enésima potencia de 2. Por ejemplo, la capacidad de Hashtable no es la enésima potencia de 2. No se puede decir qué diseño es mejor. Debería decirse que el diseñador ha integrado varios factores.Al final, optamos por utilizar la potencia de 2 como la capacidad.

Si todos los números almacenados en nuestra matriz son números pares, la capacidad de 2 elevado a n hará que todos se distribuyan en posiciones pares.

Si desea buscar una mayor eficiencia, puede usar la potencia n-ésima de 2 como capacidad, y si desea una mejor distribución de hash, puede elegir un número primo como capacidad.

¿Cuál es la diferencia entre el método put en jdk1.7 y jdk1.8?

Cuando la lista enlazada inserta un nodo, 1.7 es el método de inserción de cabeza y 1.8 es el método de inserción de cola;

1.7 es para expandirse cuando se alcanza el umbral y el contenido de la posición del subíndice de cálculo actual no está vacío, y 1.8 es para expandirse cuando se excede el umbral;

1.8 Al expandir y calcular el índice de Nodo, se optimizará 扩容时 hash & oldCapla capacidad anterior ==0的元素留在原来位置,否则新位置=旧位置+oldCap。;

¿Por qué el factor de carga está predeterminado en 0,75f?

  1. Buen equilibrio entre el uso del espacio y el tiempo de consulta
  2. Si es mayor que este valor, el espacio se guarda, pero la lista enlazada será más larga y afectará el rendimiento
  3. Si es inferior a este valor, los conflictos se reducirán, pero la expansión será más frecuente y ocupará más espacio.

¿Cuáles son los problemas con los subprocesos múltiples de HashMap?

Cuando jdk 1.7, habrá un problema de cadena muerta de expansión;

El problema de la sobrescritura del valor de la operación de escritura de subprocesos múltiples;

¿Puede la clave ser nula? ¿Cuáles son los requisitos para un objeto como clave?

La clave de HashMap puede ser nula, pero otras implementaciones de Map no lo son;

Como objeto clave, se deben implementar hashCode y equals, y el contenido de la clave no se puede modificar (inmutable);

Supongo que te gusta

Origin blog.csdn.net/TangBoBoa/article/details/130411566
Recomendado
Clasificación