Preguntas frecuentes sobre la colección Java (completadas)

1. ¿Cuáles son las colecciones comunes?

Las clases e interfaces relacionadas con la colección están todas en java.util y se dividen principalmente en tres tipos: Lista, Mapa y Conjunto.

Entre ellos Collectionse encuentra la interfaz principal de la colección List, Setque tiene principalmente dos subinterfaces:

  • List: Los elementos almacenados están ordenados y son repetibles.
  • Set: Los elementos almacenados no están desordenados y no se pueden repetir.

MapEs otra interfaz, que es una colección de estructuras de mapeo de pares clave-valor.

2.4 ¿Cómo elegir una colección?

Elegimos la colección adecuada basándonos principalmente en las características de la colección.

Por ejemplo: cuando necesite obtener el valor del elemento en función del valor clave, utilice la colección en la interfaz del Mapa.

Cuando necesite ordenar, elija TreeMap. Cuando no necesite ordenar, elija HashMap.

Si necesita garantizar la seguridad de los subprocesos, utilice ConcurrentHashMap

Cuando solo necesite almacenar valores de elementos, elija una colección que implemente la interfaz Colección.

Cuando necesite asegurarse de que los elementos sean únicos, elija una colección que implemente la interfaz Set, como TreeSet o HashSet.

Cuando no sea necesario garantizar que los elementos sean únicos, elija uno que implemente la interfaz List, como ArrayList o LinkedList.

Cuando necesite una cola, elija la colección en la interfaz Cola.

Para colas de un solo extremo, elija PriorityQueue; para colas de dos extremos, elija ArrayDeque.

2. ¿Cuál es la diferencia entre ArrayList y LinkedList?

**(1)**La estructura de datos es diferente

  • ArrayList se implementa en base a matrices
  • LinkedList se implementa en base a una lista doblemente enlazada

(2)  En la mayoría de los casos, ArrayList es más propicio para la búsqueda y LinkedList es más propicio para adiciones y eliminaciones.

  • ArrayList se implementa en función de matrices, get (int index) se puede obtener directamente a través del subíndice de la matriz y la complejidad del tiempo es O (1); LinkedList se implementa en función de listas vinculadas, y get (int index) necesita atravesar el enlace lista, y la complejidad temporal es O(n); Por supuesto, una búsqueda como get(elemento E) requiere atravesar ambas colecciones, y la complejidad temporal es O(n).

  • Si la adición o eliminación de ArrayList está al final de la matriz, simplemente inserte o elimine directamente, pero si inserta en la posición media, debe mover los elementos después de la posición de inserción hacia adelante o hacia atrás, e incluso puede desencadenar la expansión. ; lista doblemente enlazada La inserción y eliminación solo necesita cambiar el apuntamiento del nodo predecesor, el nodo sucesor y el nodo insertado, y no es necesario mover elementos.

Tenga en cuenta que puede haber una trampa aquí. LinkedList es más propicio para adiciones y eliminaciones, lo que se refleja en el tamaño de paso promedio, no en la complejidad del tiempo. La complejidad temporal de tanto las adiciones como las eliminaciones es O (n)

**(3)**Si se admite el acceso aleatorio

  • ArrayList se basa en una matriz, por lo que se puede buscar según subíndices y admite acceso aleatorio. Por supuesto, también implementa la interfaz RandmoAccess, que solo se utiliza para identificar si se admite el acceso aleatorio.
  • LinkedList se basa en una lista vinculada, por lo que no puede obtener elementos directamente según el número de serie, no implementa la interfaz RandmoAccess y la etiqueta no admite el acceso aleatorio.

**(4)** Ocupación de memoria. ArrayList se basa en arrays y es un espacio de memoria continuo. LinkedList se basa en listas enlazadas y el espacio de memoria es discontinuo. Tienen un consumo adicional en ocupación de espacio:

  • ArrayList es una matriz predefinida, puede haber espacio de memoria vacío y una cierta cantidad de espacio desperdiciado.
  • Cada nodo de LinkedList necesita almacenar su predecesor y sucesor, por lo que cada nodo ocupará más espacio.

3.¿Entiendes el mecanismo de expansión de ArrayList?

ArrayList es una colección basada en arrays. La capacidad del array se determina cuando se define. Si el array está lleno y lo vuelves a insertar, el array se desbordará. Por lo tanto, al insertar, primero verificará si es necesario expandir la capacidad. Si la capacidad actual + 1 excede la longitud de la matriz, la capacidad se expandirá.

La expansión de ArrayList consiste en crear una nueva matriz 1,5 veces más grande y luego copiar los valores de la matriz original.

4. ¿Entiendes lo que es rápido y seguro?

Fail-fast : Fail-fast es un mecanismo de detección de errores para colecciones de Java

  • Cuando se utiliza un iterador para atravesar un objeto de colección, si el subproceso B modifica (agrega, elimina o modifica) el contenido del objeto de colección durante el proceso transversal del subproceso A, se generará una excepción de modificación concurrente.
  • modCount Principio: el iterador accede directamente al contenido de la colección al atravesar y utiliza una variable durante el proceso de recorrido  . Un valor que cambia si el contenido de la colección cambia mientras se recorre modCount. Siempre que el iterador utilice hashNext()/next() para atravesar el siguiente elemento, comprobará si la variable modCount es el valor esperado de modCount. Si es así, volverá al recorrido; de lo contrario, se lanzará una excepción y el recorrido ser terminado.
  • Nota: ¡La condición de lanzamiento de esta excepción es que se detecte modCount! = condición esperada de modCount. Si el valor modCount se modifica cuando cambia la colección y resulta que está establecido en el valor esperado modCount, no se generará la excepción. Por lo tanto, la programación para operaciones concurrentes no puede depender de si se lanza esta excepción. Esta excepción solo se recomienda para detectar errores en modificaciones concurrentes.
  • Escenario: todas las clases de colección del paquete java.util son rápidas y no se pueden modificar simultáneamente en subprocesos múltiples (modificados durante el proceso de iteración), como la clase ArrayList.

a prueba de fallos

  • Los contenedores de colección que utilizan un mecanismo a prueba de fallos no acceden directamente al contenido de la colección durante el recorrido, sino que primero copian el contenido de la colección original y atraviesan la colección copiada.
  • Principio: dado que se atraviesa una copia de la colección original durante la iteración, el iterador no puede detectar las modificaciones a la colección original durante el proceso transversal, por lo que no se activará la excepción de modificación concurrente.
  • Desventajas: La ventaja de copiar contenido es que evita la excepción de modificación concurrente, pero de manera similar, el iterador no puede acceder al contenido modificado, es decir: el iterador atraviesa la copia de la colección obtenida en el momento en que comienza a atravesar. El iterador desconoce las modificaciones que se producen en la colección.
  • Escenario: todos los contenedores del paquete java.util.concurrent son a prueba de fallas y se pueden usar y modificar simultáneamente en múltiples subprocesos, como la clase CopyOnWriteArrayList.
¿Cuál es la diferencia entre iteradores rápidos y a prueba de fallos?
  • La falla rápida se realiza directamente en el contenedor. Durante el proceso de recorrido, una vez que se descubre que los datos en el contenedor están modificados, se lanzará inmediatamente una excepción ConcurrentModificationException, lo que provocará que el recorrido falle. Los contenedores comunes que utilizan el método de falla rápida incluyen HashMap y ArrayList.
  • El recorrido a prueba de fallos se basa en un clon del contenedor. Por lo tanto, la modificación del contenido del contenedor no afecta el recorrido. Los contenedores comunes que se atraviesan mediante métodos a prueba de fallos incluyen ConcurrentHashMap y CopyOnWriteArrayList.

5. ¿Cuál es la estructura de datos subyacente de HashMap?

¡7 métodos transversales y análisis de rendimiento de HashMap! (muy recomendable) (qq.com)

En JDK 7, HashMap consta de "matriz + lista vinculada", la matriz es el cuerpo principal de HashMap y la lista vinculada existe principalmente para resolver conflictos de hash.

En JDK 8, HashMap consta de "matriz + lista vinculada + árbol rojo-negro". Si la lista vinculada es demasiado larga, afectará seriamente el rendimiento de HashMap. La complejidad temporal de la búsqueda del árbol rojo-negro es O (logn), mientras que la lista vinculada es un terrible O (n). Por lo tanto, JDK 8 ha optimizado aún más la estructura de datos e introducido árboles rojo-negro. Las listas vinculadas y los árboles rojo-negro se convertirán cuando se cumplan ciertas condiciones:

  • Cuando la lista vinculada exceda 8 y la cantidad total de datos exceda 64, se convertirá en un árbol rojo-negro.
  • Antes de convertir la lista vinculada en un árbol rojo-negro, se juzgará. Si la longitud de la matriz actual es inferior a 64, elegirá expandir la matriz primero en lugar de convertirla en un árbol rojo-negro para reducir la búsqueda. tiempo.

6. ¿Por qué el umbral para convertir una lista vinculada en un árbol rojo-negro es 8?

Lo ideal es utilizar códigos hash aleatorios. La frecuencia de los nodos en el contenedor distribuidos en el depósito hash sigue la distribución de Poisson. De acuerdo con la fórmula de cálculo de la distribución de Poisson, se calcula una tabla comparativa del número y la probabilidad de los elementos en el depósito. . Puedes ver la lista enlazada. La probabilidad cuando el número de elementos en la lista enlazada es 8 ya es muy pequeña, y será aún menor cuando haya más elementos. Por lo tanto, el autor original eligió 8 al seleccionar el número de elementos de la lista enlazada, que se basó en estadísticas de probabilidad.

El tamaño de los nodos del árbol rojo-negro es aproximadamente el doble que el de los nodos ordinarios, por lo que la conversión al árbol rojo-negro sacrifica espacio por tiempo. Es más una estrategia de encubrimiento para garantizar la eficiencia de la búsqueda en situaciones extremas.

¿Por qué deberíamos elegir 8 como umbral? relacionado con las estadísticas. Idealmente, utilizando códigos hash aleatorios, los nodos en la lista vinculada se ajustan a la distribución de Poisson y la probabilidad del número de nodos disminuye. Cuando el número de nodos es 8, la probabilidad de ocurrencia es solo 0.00000006.

En cuanto al umbral para convertir un árbol rojo-negro nuevamente en una lista vinculada, ¿por qué es 6 en lugar de 8? Esto se debe a que si este umbral también se establece en 8, si ocurre una colisión y el aumento o disminución del nodo es alrededor de 8, se producirá una conversión continua de la lista vinculada y el árbol rojo-negro, lo que resultará en un desperdicio de recursos.

7. ¿Por qué no utilizar árboles rojo-negro directamente al resolver conflictos de hash? ¿Y elegir usar la lista vinculada primero y luego convertirla a un árbol rojo-negro?

Porque el árbol rojo-negro necesita realizar operaciones como rotación a la izquierda, rotación a la derecha y cambio de color para mantener el equilibrio, pero la lista enlazada individualmente no.

Cuando hay menos de 8 elementos, al realizar operaciones de consulta, la estructura de la lista vinculada ya puede garantizar el rendimiento de la consulta. Cuando hay más de 8 elementos, la complejidad del tiempo de búsqueda del árbol rojo-negro es O (logn), mientras que la lista vinculada es O (n), en este momento se necesita el árbol rojo-negro para acelerar la consulta. , pero la eficiencia de agregar nuevos nodos se vuelve más lenta.

Por lo tanto, si se utiliza una estructura de árbol rojo-negro desde el principio, habrá muy pocos elementos y la eficiencia de las nuevas incorporaciones será relativamente lenta, lo que sin duda será una pérdida de rendimiento.

8. ¿Por qué es necesario aplicar AND al valor hash con longitud-1?

  • Módulo del valor hash a la longitud de la matriz. La operación de módulo es muy costosa y no tan rápida como la operación de bits.
  • Cuando la longitud es siempre 2 elevada a la enésima potencia, h& (length-1) la operación equivale a tomar la longitud del módulo, es decir, h%longitud, pero & es más eficiente que %.

9. ¿Cómo se calcula el índice de almacenamiento de la clave en HashMap?

Primero calcule el valor del código hash según el valor clave, luego calcule el valor hash según el código hash y finalmente calcule la ubicación de almacenamiento mediante hash & (longitud-1)

10. ¿Cuál es el factor de carga predeterminado de HashMap? ¿Por qué es 0,75 y no 0,6 o 0,8?

Como regla general, el factor de carga predeterminado (0,75) proporciona un buen compromiso entre costo de tiempo y espacio.

¿Por qué el factor de carga de HashMap debe ser 0,75? ¿En lugar de 0,8, 0,6?

11. ¿Cuáles son las formas de resolver conflictos de hash? ¿Qué HashMap usar?

Los métodos para resolver conflictos Hash son:

  • Método de direccionamiento abierto: también llamado método de re-hash. La idea básica es que si p = H (clave) entra en conflicto, use p como base y vuelva a hacer hash, p1 = H (p). Si p1 vuelve a entrar en conflicto, entonces Basado en p1, y así sucesivamente, hasta que se encuentre una dirección hash pi que no entre en conflicto. Por lo tanto, la longitud de la tabla hash requerida por el método de direccionamiento abierto debe ser mayor o igual que los elementos que deben almacenarse y, debido a que hay un nuevo hash, el nodo eliminado solo se puede marcar, pero el nodo no se puede realmente eliminado.
  • Método de repetición: hash doble, hash múltiple, que proporciona múltiples funciones hash diferentes. Cuando R1 = H1 (clave1) entra en conflicto, calcule R2 = H2 (clave1) hasta que no haya conflicto. Aunque es menos probable que esto provoque acumulación, aumenta el tiempo de cálculo.
  • Método de dirección de cadena: método de cremallera, los elementos con el mismo valor hash forman una lista de sinónimos enlazada individualmente, y el puntero principal de la lista enlazada individualmente se almacena en la unidad i-ésima de la tabla hash. La búsqueda, inserción y eliminación son principalmente en la lista enlazada de sinónimos conducta. El método de lista vinculada es adecuado para situaciones donde las inserciones y eliminaciones son frecuentes.
  • Establezca un área de desbordamiento pública: divida la tabla hash en una tabla pública y una tabla de desbordamiento. Cuando ocurre un desbordamiento, todos los datos de desbordamiento se colocarán en el área de desbordamiento.

El método de dirección en cadena se utiliza en HashMap.

¿Por qué la longitud de la matriz HashMap es una potencia de 2?

2 elevado a la potencia N ayuda a reducir la posibilidad de colisión. Si la longitud es una potencia de 2, la longitud-1 debe convertirse a binario en la forma 11111..., y la operación binaria AND con h será muy rápida y no se desperdiciará espacio. Pongamos un ejemplo, mira la imagen a continuación:

Cuando la longitud = 15, los resultados de 6 y 7 son los mismos, lo que significa que sus ubicaciones de almacenamiento en la tabla son las mismas, es decir, se produce una colisión. 6 y 7 formarán una lista vinculada en una ubicación y los resultados de 4 y 5 también son iguales, lo que reducirá la velocidad de consulta.

Si analizamos más a fondo, también encontraremos que el espacio se desperdicia mucho. Tomando longitud = 15 como ejemplo, no hay datos almacenados en ocho ubicaciones: 1, 3, 5, 7, 9, 11, 13 y 15. . Porque cuando se aplica AND al valor hash con 14 (es decir, 1110), el último dígito del resultado es siempre 0, es decir, es imposible almacenar datos en las posiciones 0001, 0011, 0101, 0111, 1001, 1011, 1101, y 1111. .

12. ¿Qué se utiliza generalmente como clave de HashMap?

Generalmente, las clases inmutables como Integer y String se utilizan como claves de HashMap, y String es la más común.

  • Debido a que la cadena es inmutable, el código hash se almacena en caché cuando se crea y no es necesario volver a calcularlo.
  • Debido a que los métodos equals() y hashCode() se utilizan al obtener el objeto, es muy importante que el objeto clave anule correctamente estos dos métodos. Clases como Integer y String han reescrito los métodos hashCode() y equals() de una forma muy estandarizada.

13. ¿Por qué HashMap no es seguro para subprocesos?

  • En JDK 7, la expansión bajo subprocesos múltiples provocará un bucle infinito.
  • La colocación de subprocesos múltiples puede provocar la pérdida de elementos.
  • Cuando put y get son concurrentes, get puede ser nulo.

14. ¿Cuál es el proceso del método put de HashMap?

Tomando JDK 8 como ejemplo, el breve proceso es el siguiente:

1. Primero, calcule el valor hash según el valor de la clave y busque el subíndice del elemento almacenado en la matriz;

2. Si la matriz está vacía, llame a redimensionar para la inicialización;

3. Si no hay conflicto de hash, colóquelo directamente en el subíndice de matriz correspondiente;

4. Si hay un conflicto y la clave ya existe, se sobrescribirá el valor;

5. Si se descubre que el nodo es un árbol rojo-negro después de un conflicto, cuelgue el nodo en el árbol;

6. Si el conflicto es una lista vinculada, determine si la lista vinculada es mayor que 8. Si es mayor que 8 y la capacidad de la matriz es menor que 64, amplíela; si los nodos de la lista vinculada son mayores que 8 y la matriz la capacidad es mayor que 64, convierta esta estructura en un árbol rojo-negro; de lo contrario, el par clave-valor se inserta en la lista vinculada y, si la clave existe, el valor se sobrescribe.

15.¿Cuál es la diferencia entre arrayList y linkedList? 

  • 1.ArrayList se basa en matrices y el espacio de almacenamiento es continuo. LinkedList se basa en una lista vinculada y el espacio de almacenamiento es discontinuo. (LinkedList es una lista doblemente enlazada)

  • 2. Para obtener y configurar el acceso aleatorio  , ArrayList es mejor que LinkedList porque LinkedList necesita mover el puntero.

  • 3. Para las operaciones nuevas y de eliminación de agregar y eliminar, LinedList tiene una ventaja porque ArrayList necesita mover datos.

  • 4. Para la misma cantidad de datos, LinkedList puede ocupar menos espacio, porque ArrayList necesita reservar espacio para la adición de datos posteriores, mientras que LinkedList solo necesita agregar un nodo para agregar datos.

16. ¿Cuál es la diferencia entre Colección y Colecciones?

  • La colección es una interfaz de colección que proporciona métodos de interfaz comunes para operaciones básicas en objetos de la colección. Todas las colecciones son sus subclases, como Lista, Conjunto, etc.
  • Colecciones es una clase contenedora que contiene muchos métodos estáticos y no se pueden crear instancias. En su lugar, se utiliza como una clase de herramienta, como el método de clasificación proporcionado: Collections.sort(list); el método de reversión proporcionado: Collections.reverse(list). .

17. En HashSet, ¿cuál es la relación entre iguales y hashCode?

Ambos métodos igual y hashCode se heredan de la clase de objeto. igual se utiliza principalmente para determinar si la referencia de la dirección de memoria del objeto es la misma dirección; hashCode convierte la dirección de memoria del objeto en un hash de acuerdo con las reglas hash definidas. code . Los elementos almacenados en HashSet no se pueden repetir. Los métodos hashCode y iguales se utilizan principalmente para determinar si los objetos almacenados son iguales:

  • Si los valores de hashCode de dos objetos son diferentes, significa que los dos objetos no son iguales.
  • Si los valores de hashCode de los dos objetos son iguales, se llamará al método igual del objeto. Si el resultado devuelto por el método igual es verdadero, entonces los dos objetos son iguales; de lo contrario, no son iguales.

18. ¿Cuál es la diferencia entre HashMap y Hashtable?

  • ¿Es seguro para subprocesos?
  • HashMap no es seguro para subprocesos, Hashtable es seguro para subprocesos, porque los métodos internos de Hashtable están básicamente sincronizados. (¡Si desea garantizar la seguridad de los subprocesos, utilice ConcurrentHashMap! Hashtable está obsoleto);
  • eficiencia:
  • Debido a problemas de seguridad de subprocesos, HashMap es más eficiente que Hashtable. Además, Hashtable es básicamente obsoleto, no lo uses en tu código;
  • Soporte para clave nula y valor nulo:
  • HashMap puede almacenar claves y valores nulos, pero solo puede haber un nulo como clave y varios valores nulos; Hashtable no permite claves nulas ni valores nulos; de lo contrario, se generará una excepción NullPointerException.
  • La diferencia entre el tamaño de capacidad inicial y cada tamaño de capacidad de expansión:
  • Si no se especifica el valor de capacidad inicial al crear, el tamaño inicial predeterminado de Hashtable es 11. Después de cada expansión, la capacidad pasa a ser la 2n+1 original. El tamaño de inicialización predeterminado de HashMap es 16. Cada ampliación posterior duplicará la capacidad. Si se proporciona un valor inicial de capacidad al crear, Hashtable usará directamente el tamaño que usted proporcionó y HashMap lo expandirá a una potencia de 2.
  • Estructura de datos subyacente:
  • HashMap después de JDK1.8 ha experimentado cambios importantes en la resolución de conflictos de hash. Cuando la longitud de la lista vinculada es mayor que el umbral (el valor predeterminado es 8), la lista vinculada se convertirá en un árbol rojo-negro (se juzgará antes convertir la lista vinculada en un árbol rojo-negro. Si la longitud de la matriz actual es inferior a 64, elegirá expandir la matriz primero en lugar de convertirla a un árbol rojo-negro) para reducir el tiempo de búsqueda. Hashtable no tiene tal mecanismo. .

19. ¿Cuáles son las similitudes y diferencias entre ArrayList, Vector y LinkedList?

20. Describe brevemente el conjunto de Java.

Set es una colección. Esta estructura de datos no permite elementos duplicados y desordenados. Java tiene tres formas de implementar Set:

HashSet se implementa a través de HashMap. La clave de HashMap es el elemento almacenado en HashSet. El sistema de valores define una constante de tipo Objeto llamada PRESENTE. Al juzgar si los elementos son iguales, primero compare el código hash y luego use iguales para comparar si son iguales. La consulta es O (1)

LinkedHashSet hereda de HashSet, se implementa a través de LinkedHashMap y utiliza una lista doblemente vinculada para mantener el orden de inserción de los elementos.

TreeSet se implementa a través de TreeMap. La estructura de datos subyacente es un árbol rojo-negro. Al agregar elementos al conjunto, se inserta en la posición apropiada de acuerdo con las reglas de comparación para garantizar que el conjunto insertado todavía esté ordenado. Consulta O (iniciar sesión)

21. Describa brevemente el HashMap de Java

Antes de JDK8, la implementación subyacente era matriz + lista vinculada, pero JDK8 la cambió a matriz + lista vinculada/árbol rojo-negro. Las principales variables miembro incluyen la matriz de tabla para almacenar datos, el tamaño del número de elementos y el factor de carga loadFactor. Los datos en HashMap existen en forma de pares clave-valor. El valor hash correspondiente a la clave se utiliza para calcular el subíndice de la matriz. Si el valor hash de la clave de dos elementos es el mismo, se producirá un conflicto hash y ser colocado en la misma lista enlazada.

La matriz de tabla registra datos de HashMap. Cada subíndice corresponde a una lista vinculada. Todos los datos de conflicto de hash se almacenarán en la misma lista vinculada. El nodo Nodo/Entrada contiene cuatro variables miembro: clave, valor, siguiente puntero y valor hash. Después de JDK8, las listas vinculadas superiores a 8 se convertirán en árboles rojo-negro.

Si los datos actuales/capacidad total de datos > factor de carga, Hashmap realizará la operación de expansión. La capacidad de inicialización predeterminada es 16, la capacidad de expansión debe ser una potencia de 2, la capacidad máxima es 1<< 30 y el factor de carga predeterminado es 0,75.

22. ¿Por qué HashMap no es seguro para subprocesos?

HashMap no es seguro para subprocesos y pueden ocurrir estos problemas:

  • Bucle infinito de expansión bajo subprocesos múltiples. HashMap en JDK1.7 utiliza el método de inserción de encabezado para insertar elementos. En un entorno de subprocesos múltiples, la expansión puede provocar la aparición de una lista enlazada circular, formando un bucle infinito. Por lo tanto, JDK1.8 utiliza el método de inserción de cola para insertar elementos, lo que mantendrá el orden original de los elementos de la lista vinculada durante la expansión y no causará el problema de las listas vinculadas circulares.

  • La colocación de subprocesos múltiples puede provocar la pérdida de elementos. Varios subprocesos ejecutan operaciones de colocación al mismo tiempo. Si las posiciones del índice calculadas son las mismas, la clave siguiente sobrescribirá la clave anterior, lo que provocará la pérdida de elementos. Este problema existe tanto en JDK 1.7 como en JDK 1.8.

  • Cuando put y get se ejecutan simultáneamente, get puede ser nulo. Cuando el subproceso 1 ejecuta put, se produce un refrito porque el número de elementos excede el umbral. El subproceso 2 ejecuta get en este momento, lo que puede causar este problema. Este problema existe tanto en JDK 1.7 como en JDK 1.8.

23. Describe brevemente el TreeMap de Java

TreeMap es una estructura de mapa implementada utilizando un árbol rojo-negro en la parte inferior. La implementación subyacente es un árbol binario ordenado equilibrado. Dado que la complejidad del tiempo de inserción, eliminación y recorrido del árbol rojo-negro es O (logN), su rendimiento es menor que el de una tabla hash. Sin embargo, la tabla hash no puede proporcionar una salida ordenada de pares clave-valor, mientras que el árbol rojo-negro puede proporcionar una salida ordenada según el tamaño del valor clave.

24. ¿Cuáles son las similitudes y diferencias entre ArrayList, Vector y LinkedList?

  • ArrayList, Vector y LinkedList son matrices escalables, es decir, matrices cuya longitud se puede cambiar dinámicamente.
  • ArrayList y Vector se implementan en función de la matriz Object [] que almacena elementos, abrirán un espacio continuo en la memoria para almacenamiento y admitirán acceso a índices y subíndices. Pero cuando se trata de insertar elementos, es posible que necesite mover los elementos en el contenedor y la eficiencia de inserción es baja. Cuando los elementos almacenados exceden la capacidad inicial del contenedor, tanto ArrayList como Vector se expandirán.
  • Vector es seguro para subprocesos y la mayoría de sus métodos están sincronizados directa o indirectamente. ArrayList no es seguro para subprocesos y sus métodos no están sincronizados. LinkedList tampoco es seguro para subprocesos.
  • LinkedList se implementa utilizando una lista bidireccional y el índice de datos debe atravesarse desde el principio, por lo que la eficiencia del acceso aleatorio es baja, pero al insertar elementos, no es necesario mover los datos y la eficiencia de inserción es alta. .

25. La diferencia entre HashMap y HashSet

La capa inferior de HashSet se implementa en base a HashMap.

Implementar interfaces de manera diferente

HashMap implementa la interfaz Map

HashSet implementa la interfaz Set

almacenado de manera diferente

HashMap almacena pares clave-valor

Objeto de almacenamiento HashSet

Sumar de diferentes maneras

HashMap llama a put() para agregar elementos

HashSet llama a add() para agregar elementos

Calcule HashCode de manera diferente

HashMap usa clave para calcular el código hash

HashSet utiliza objetos miembro para calcular los valores del código hash. Para dos objetos diferentes, el código hash puede ser el mismo, por lo que el método equals() también se utiliza para determinar la igualdad de los objetos.

26. ¿Qué optimizaciones principales ha realizado jdk1.8 en HashMap? ¿Por qué?

El HashMap de jdk1.8 tiene cinco puntos principales de optimización:

  1. Estructura de datos : matriz + lista vinculada cambiada a matriz + lista vinculada o árbol rojo-negro

    原因O(n): Cuando ocurre un conflicto de hash, los elementos se almacenarán en la lista vinculada. Si la lista vinculada es demasiado larga, se convertirá en un árbol rojo-negro, lo que reducirá la complejidad del tiempo deO(logn)

  2. Método de inserción de lista vinculada : el método de inserción de la lista vinculada se cambia del método de inserción de cabecera al método de inserción de cola.

    En pocas palabras, al insertar, si ya hay un elemento en la posición de la matriz, 1.7 coloca el nuevo elemento en la matriz y el nodo original se usa como el nodo sucesor del nuevo nodo. 1.8 atraviesa la lista vinculada y coloca el elemento al final de la lista enlazada.

    原因: Porque cuando se expande el método de conexión del cabezal 1.7, el método de conexión del cabezal invertirá la lista vinculada y se producirá un bucle en un entorno de subprocesos múltiples.

  3. Refrito de expansión : al expandirse, 1.7 necesita repetir los elementos en la matriz original y colocarlos en la posición de la nueva matriz. 1.8 usa una lógica de juicio más simple y no necesita volver a calcular la posición a través de la función hash. La nueva posición permanece sin cambios o el índice + Tamaño de capacidad agregada.

    原因:Mejore la eficiencia de la expansión de la capacidad y amplíe la capacidad más rápidamente.

  4. Tiempo de expansión : Al insertar, 1.7 primero determina si se necesita expansión antes de insertar, 1.8 inserta primero y luego determina si se necesita expansión una vez completada la inserción;

  5. Función hash : 1.7 realiza cuatro cambios y cuatro XOR, jdk1.8 solo lo hace una vez.

    原因: Si lo haces 4 veces la utilidad marginal no será grande, cámbiala a una vez para mejorar la eficiencia.

27. ¿Qué otros métodos de construcción de funciones hash conoces?

El método del constructor hash en HashMap se llama:

  • Excepto por el método del resto : H (clave) = clave% p (p <= N), la palabra clave se divide por un entero positivo p que no es mayor que la longitud de la tabla hash y el resto es la dirección. Por supuesto, HashMap se ha optimizado y transformado, hashing más eficiente y más equilibrado.

Además, existen varios métodos comunes de construcción de funciones hash:

  • método de direccionamiento directo

    Asigne directamente keya la posición de la matriz correspondiente, por ejemplo, 1232 se coloca en la posición del subíndice 1232.

  • analítica digital

    Tome keyciertos números (como decenas y centenas) como ubicación del mapeo

  • Método cuadrado-medio

    Tome keylos dígitos del medio del cuadrado como posición de mapeo

  • método de plegado

    Se keydividirá en varios segmentos con el mismo número de dígitos y luego su suma de superposición se utilizará como posición de mapeo.

28. ¿Cuánto sabes sobre los árboles rojo-negros? ¿Por qué no utilizar árbol binario/árbol equilibrado?

El árbol rojo-negro es esencialmente un árbol de búsqueda binario y, para mantener el equilibrio, agrega algunas reglas basadas en el árbol de búsqueda binario:

  1. Cada nodo es rojo o negro;
  2. El nodo raíz siempre es negro;
  3. Todos los nodos de hoja son negros (tenga en cuenta que los nodos de hoja mencionados aquí son en realidad nodos NULL en el gráfico);
  4. Los dos nodos secundarios de cada nodo rojo deben ser negros;
  5. La ruta desde cualquier nodo a cada nodo hoja en su subárbol contiene la misma cantidad de nodos negros;

La razón por la que no se utilizan árboles binarios:

Un árbol rojo-negro es un árbol binario equilibrado. La complejidad temporal en el peor de los casos de inserción, eliminación y búsqueda es O (logn), lo que evita la complejidad temporal O (n) en el peor de los casos de un árbol binario.

¿Por qué no equilibrar los árboles binarios?

Un árbol binario equilibrado es un árbol más estrictamente equilibrado que un árbol rojo-negro. Para mantener el equilibrio, se requieren más rotaciones. Esto significa que un árbol binario equilibrado es menos eficiente para mantener el equilibrio, por lo que la eficiencia de inserción y eliminación de un El árbol binario balanceado es más alto que el de un árbol rojo-negro.

¿Sabes cómo mantiene el equilibrio el árbol rojo-negro?

Los árboles rojo-negros mantienen el equilibrio de dos maneras: 旋转y 染色.

  • Rotación: Hay dos tipos de rotación, hacia la izquierda y hacia la derecha.

 29.¿Están ordenados los nodos internos de HashMap?

HashMap está desordenado y se inserta aleatoriamente según el valor hash. Si desea utilizar un mapa ordenado, puede utilizar LinkedHashMap o TreeMap.

30. HashMap no es seguro para subprocesos y pueden ocurrir estos problemas:

  • Bucle infinito de expansión bajo subprocesos múltiples. HashMap en JDK1.7 utiliza el método de inserción de encabezado para insertar elementos. En un entorno de subprocesos múltiples, la expansión puede provocar la aparición de una lista enlazada circular, formando un bucle infinito. Por lo tanto, JDK1.8 utiliza el método de inserción de cola para insertar elementos, lo que mantendrá el orden original de los elementos de la lista vinculada durante la expansión y no causará el problema de las listas vinculadas circulares.

  • La colocación de subprocesos múltiples puede provocar la pérdida de elementos. Varios subprocesos ejecutan operaciones de colocación al mismo tiempo. Si las posiciones del índice calculadas son las mismas, la clave siguiente sobrescribirá la clave anterior, lo que provocará la pérdida de elementos. Este problema existe tanto en JDK 1.7 como en JDK 1.8.

  • Cuando put y get se ejecutan simultáneamente, get puede ser nulo. Cuando el subproceso 1 ejecuta put, se produce un refrito porque el número de elementos excede el umbral. El subproceso 2 ejecuta get en este momento, lo que puede causar este problema. Este problema existe tanto en JDK 1.7 como en JDK 1.8.

Supongo que te gusta

Origin blog.csdn.net/pachupingminku/article/details/132692497
Recomendado
Clasificación