Colección de preguntas de entrevista de código fuente de mapa

1. ¿Cuál es la estructura de datos subyacente de HashMap?
Respuesta: La estructura de datos subyacente de HashMap es una combinación de matrices, listas enlazadas y árboles rojo-negro. La función principal de las matrices es facilitar la búsqueda rápida. La complejidad del tiempo es O (1), el tamaño predeterminado es 16 y el índice de subíndice de la matriz es el código hash de la clave. computacional. El elemento de la matriz se llama Nodo. ​​Cuando varias claves tienen el mismo código hash pero diferentes valores de clave, un solo Nodo se convertirá en una lista vinculada. La complejidad de la consulta de la lista vinculada es O (n). Cuando la longitud de la lista vinculada es mayor o igual a 8 y el tamaño de la matriz excede 64 , La lista enlazada se transformará en un árbol rojo-negro. La complejidad de la consulta del árbol rojo-negro es O (log (n)). En pocas palabras, los peores tiempos de consulta equivalen a la profundidad máxima del árbol rojo-negro.

2. ¿Cuál es la diferencia entre HashMap, TreeMap y LinkedHashMap?
Respuesta: Los tres usarán árboles rojo-negro bajo ciertas circunstancias, y el algoritmo hash subyacente es el mismo. Durante el proceso de iteración, si se cambia la estructura de datos del mapa, se informará ConcurrentModificationException.
La diferencia es que la estructura de datos HashMap es principalmente una matriz, y la consulta es muy rápida. La estructura de datos TreeMap es principalmente un árbol rojo-negro. Utiliza las características del árbol rojo-negro para ser pequeño a la izquierda y grande a la derecha para lograr la clasificación de claves. LinkedHashMap se basa en HashMap. Se incrementa la estructura de la lista enlazada y se realizan las dos estrategias de acceso a la orden de inserción y eliminación de acceso mínimo. Debido a la diferencia en la estructura de datos subyacente de los tres mapas, los escenarios de uso de los tres son diferentes. TreeMap es adecuado para escenas que deben clasificarse según la clave, LinkedHashMap es adecuado para escenas que acceden según el orden de inserción o necesitan eliminar los elementos menos visitados, y las escenas restantes usan HashMap.

3. ¿Cuéntame sobre el algoritmo hash de Map?
Respuesta: En el código fuente, el hash se calcula mediante el siguiente código. Primero, se calcula el código hash de la clave. Debido a que la clave es Objeto, el código hash se calcula de acuerdo con los diferentes tipos de claves, y luego se calcula h ^ (h >>> 16), entonces La ventaja de hacer esto es garantizar que el valor hash calculado esté relativamente disperso en la mayoría de los escenarios.
En términos generales, después de calcular el valor hash, es necesario calcular la posición del subíndice de índice de la clave actual en la matriz. Aquí, se adopta el método de módulo. Posición del subíndice de índice = valor de hash% tamaño de matriz. La ventaja es que el índice calculado puede garantizarse. El valor estándar se distribuye uniformemente en cada posición de índice de la matriz, pero el cálculo de la operación del módulo es relativamente lento. Hay una fórmula matemática. Cuando b es una potencia de 2, a% b = a & (b- 1), por lo que la fórmula de cálculo de la posición del índice aquí se puede reemplazar con (n-1) & hash.

static final int hash(Object key) {
    
    
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

4. ¿Por qué no utilizar key% array size, pero es necesario utilizar key hash value% array size?
Respuesta: Si la clave es un número, usar directamente el% de tamaño de matriz de clave no es ningún problema, pero la clave también puede ser una cadena o un objeto complejo. En este momento, usar una cadena o un objeto complejo% de tamaño de matriz no es aceptable, por lo que primero debe Calcule el valor hash de la clave.

5. ¿Por qué necesitamos desplazar 16 bits a la derecha al calcular el valor hash?
Respuesta: El algoritmo de hash es h ^ (h >>> 16). Para hacer que el valor de hash calculado sea más disperso, elegimos primero desplazar h sin signo en 16 bits, y luego XOR con h para hacer h Tanto los 16 bits altos como los 16 bits bajos participan en el cálculo, reduciendo la posibilidad de colisión.

6. ¿Por qué la operación de módulo se reemplaza con operación &?
Respuesta: El valor hash calculado por key.hashCode () no es el subíndice del índice de la matriz. Para calcular la posición del subíndice del índice de forma aleatoria, también es necesario usar el valor hash y el tamaño de la matriz para tomar el módulo, de modo que la distribución del subíndice del índice calculado Será más parejo. El cálculo del procesador de operación de módulo es relativamente lento y se reemplaza por operación &, que está respaldada por pruebas matemáticas, lo que mejora la velocidad de procesamiento del procesador.

7. ¿Por qué se recomienda que el tamaño de la matriz sea una potencia de 2?
Respuesta: Porque solo cuando el tamaño es una potencia de 2, el hash% n (tamaño de la matriz) = (n-1) y el hash se mantienen verdaderos.

8. ¿Cómo se expande HashMap?
Respuesta: Hay dos oportunidades para la expansión. Una es cuando se encuentra que la matriz está vacía durante la colocación y la expansión se realiza durante la inicialización. El tamaño de expansión predeterminado es 16. En segundo lugar, después de que la colocación es exitosa, cuando se encuentra que el tamaño de la matriz existente es mayor que el umbral de expansión, se realiza la expansión y la expansión es el doble del tamaño de la matriz anterior. El umbral para la expansión es el umbral, y el umbral se volverá a calcular cada vez que se expanda. El umbral es igual al tamaño de la matriz * factor de impacto (0,75). Después de que se inicializa la nueva matriz, los valores de la matriz anterior deben copiarse en la nueva matriz.Las listas vinculadas y los árboles rojo-negro tienen sus propios métodos de copia.

9. ¿Qué hacer cuando hay un conflicto de hash?
Respuesta: El conflicto de hash se refiere a la situación en la que el cálculo del código hash del valor clave es el mismo, pero el valor clave es diferente. Si solo hay un elemento en el depósito o ya es una lista vinculada, el nuevo elemento se agrega directamente al final de la lista vinculada. Si los elementos en el depósito ya son listas vinculadas y la cantidad de listas vinculadas es mayor o igual a 8, hay dos situaciones en este momento. Si el tamaño de la matriz es menor que 64 en este momento, la matriz se expande nuevamente y la lista vinculada no se convertirá en un árbol rojo-negro. Si el tamaño de la matriz es mayor que 64, la lista vinculada se convertirá en un árbol rojo-negro. Aquí no solo juzga si el número de listas enlazadas es mayor o igual a 8, sino que también juzga el tamaño de la matriz. La razón por la que la capacidad de la matriz es menor que 64 no se convierte inmediatamente. Se supone que el árbol rojo-negro ocupa mucho más espacio que la lista vinculada, y la conversión también lleva mucho tiempo. Por lo tanto, si la capacidad de la matriz es pequeña, el conflicto es serio, puede intentar expandir la capacidad primero.

10. ¿Por qué la lista enlazada debería transformarse en un árbol rojo-negro cuando el número de listas enlazadas es mayor o igual a 8?
Respuesta: Cuando hay demasiadas listas vinculadas, el recorrido llevará más tiempo. La conversión a un árbol rojo-negro puede reducir la complejidad de tiempo del recorrido, pero la conversión a un árbol rojo-negro tiene costos de espacio y tiempo para la conversión, que se distribuye a través de Poisson. Calculada por la fórmula, en circunstancias normales, la probabilidad de que el número de listas vinculadas aparezca 8 es menor que uno en diez millones, por lo que en circunstancias normales, las listas vinculadas no se transformarán en árboles rojo-negro. El propósito de este diseño es evitar situaciones anormales (como el algoritmo hash no es aplicable), cuando el número de listas enlazadas es mayor o igual a 8, aún se puede recorrer rápidamente.

11. ¿Cuándo se convertirá el árbol rojo-negro en una lista vinculada?
Respuesta: Cuando el número de nodos es menor o igual a 6, el árbol rojo-negro se convertirá automáticamente en una lista vinculada. La consideración principal es el costo de espacio del árbol rojo-negro. Cuando el número de nodos es menor o igual a 6 Al recorrer la lista enlazada será muy rápido, por lo que el árbol rojo-negro volverá a convertirse en una lista enlazada.

12. Cuando se coloca HashMap, ¿qué debo hacer si la clave ya existe en la matriz y no quiero sobrescribir el valor? ¿Qué debo hacer si quiero volver al valor predeterminado cuando el valor obtenido está vacío?
Respuesta: Si la matriz tiene una clave, pero no desea sobrescribir el valor, puede elegir el método putIfAbsent. Este método tiene una variable incorporada onlyIfAbsent. Si la variable incorporada es verdadera, no se sobrescribirá. El método put que se usa habitualmente, el método integrado onlyIfAbsent es falso, puede sobrescribir .
Al tomar un valor, si está vacío y desea volver al valor predeterminado, puede usar el método getOrDefault. El primer parámetro del método es clave y el segundo parámetro es el valor predeterminado devuelto, como map.getOrDefault ("2", "0") , Cuando no hay un valor de la clave 2 en el mapa, devolverá 0 por defecto en lugar de vacío.

13. ¿Qué significa LRU en LinkedHashMap y cómo se implementa?
Respuesta: LRU (Usado menos recientemente), también llamado la estrategia de eliminación de acceso mínimo en LinkedHashMap, puede establecer una determinada estrategia a través del método removeEldestEntry, de modo que el elemento al que se accede menos se elimine en el momento adecuado.
Al final de la ejecución del método put, LinkedHashMap verificará esta estrategia y eliminará el nodo principal si cumple con la estrategia. Cuando se obtiene LinkedHashMap, moverá el nodo visitado actualmente al final de la lista vinculada. Lentamente, el nodo principal será el elemento menos visitado.

Supongo que te gusta

Origin blog.csdn.net/Jgx1214/article/details/109096678
Recomendado
Clasificación