Este artículo explora e introduce varios principios e ideas de programación paso a paso explorando el uso y los problemas existentes de HashMap. Los principales contenidos incluyen:
-
- ¿Qué es HashMap?
- Cómo expandir HashMap
- Por qué la capacidad inicial de HashMap es 16 por defecto
- ¿Por qué el factor de carga es 0,75?
- Implementación específica del método hash
- LinkedHashMap implementa el almacenamiento en caché LRU
- Problemas y soluciones en HashMap
-
- Bloqueo de segmento
- CAS + sincronizado
- Principio de construcción inicial
Explore HashMap
-
1. [Qué es HashMap]
- Estructura de datos: HashMap es una estructura de datos de matriz y lista enlazada (método de dirección de cadena).
- Uso: agregue datos primero a través del hash y colóquelos en la posición calculada. Si la posición tiene un valor, se produce un conflicto de hash. Generalmente, hay cuatro formas de manejar el conflicto de hash:
- Método de dirección en cadena: esta dirección almacena una lista vinculada y puede agregar datos directamente al final de la lista vinculada .
- Método de direccionamiento abierto: posponga la siguiente dirección no conflictiva.
- refrito: usa otro algoritmo hash
- Establezca un área de desbordamiento común: divida la tabla hash en dos partes: la tabla básica y la tabla de desbordamiento Todos los elementos que entren en conflicto con la tabla básica se llenan en la tabla de desbordamiento.
-
2. [Cómo expandir HashMap]
- Hay una matriz en el HashMap, y la lista vinculada se cuelga en la matriz. La matriz tiene un valor establecido y la lista vinculada también tiene una longitud de expansión. Cuando la longitud de la lista vinculada es mayor que 8, la longitud de la matriz se evalúa además,
- La longitud de la matriz es menor que el valor establecido MIN_TREEIFY_CAPACITY (64), simplemente expanda la matriz y vuelva a aplicar hash a los datos almacenados.
- Con cada expansión, la longitud de la matriz se duplica.
- Si sabe de antemano que desea almacenar 1000, es más razonable establecer un HashMap: 1000 <0.75x, encontrar x es 1334, nuevo HashMap (1334) y cambiar automáticamente el tamaño de almacenamiento a la potencia más pequeña de 2 mayor que 1334: 2048 .
- Cuando la longitud de la matriz supera los 64, la lista vinculada se convierte en un árbol rojo-negro.
- Referencia: ¿Cuándo se transformará la lista vinculada en HashMap en un árbol rojo-negro?
-
2.1. [Por qué la capacidad inicial de HashMap es 16 por defecto]
- ¿Por qué podemos usar la operación de bits (&) para implementar la operación de módulo (%)? El principio de esta realización es el siguiente:
- X% 2 ^ n = X & (2 ^ n - 1)
- Elija 16 es el valor de la experiencia
- Cómo encontrar la potencia más pequeña de 2 mayor que el valor entrante: problema matemático, no.
- ¿Por qué podemos usar la operación de bits (&) para implementar la operación de módulo (%)? El principio de esta realización es el siguiente:
// 找到第一个大于等于initialCapacity的2的平方的数
int capacity = 1;
while (capacity < initialCapacity)
capacity <<= 1;
-
2.2. [Por qué el factor de carga es 0,75]
- Debido a que la complejidad del tiempo y la complejidad del espacio se consideran de manera integral, se elige 0,75 como compromiso.
-
2.3 [La implementación específica del método hash]
static int indexFor(int h, int length) {
return h & (length - 1);
}
-
Por que es length-1
- Debido a que la longitud es generalmente una potencia de 2, hará que los datos impares sean menores que los hash.
-
3. [LinkedHashMap implementa el almacenamiento en caché de LRU]
- LinkedHashMap admite dos tipos de orden de inserción y orden de acceso
- Orden de inserción: el que se agregó primero está en el frente y el que se agregó después está en la parte posterior. La operación de modificación no afecta el pedido.
- Secuencia de acceso: el llamado acceso se refiere a la operación de obtención / colocación. Después de realizar la operación de obtención / colocación en una clave, el par clave-valor correspondiente se moverá al final de la lista vinculada, por lo que el último es el accedido más recientemente y el primero es el más largo. Los que no han sido visitados, este es el orden de visitas.
- Referencia: uso básico de LinkedHashMap y uso para realizar un almacenamiento en caché simple
- LinkedHashMap admite dos tipos de orden de inserción y orden de acceso
-
4. [Problemas y soluciones de HashMap en el caso de multiproceso]
- 4.1. Problemas existentes: en un entorno de subprocesos múltiples,
- (1) La operación multiproceso de HashMap provocará datos sucios.
- (2) El uso de Hashmap para la operación de colocación puede causar un bucle sin fin, lo que da como resultado una utilización de la CPU cercana al 100%, por lo que HashMap no se puede usar en condiciones concurrentes. Antes de JDK1.8, HashMap concurrente multiproceso tendrá un bucle infinito
- 4.2. Solución
- Use Hashtable, use sincronizado para garantizar la seguridad de los subprocesos.
- Pero el uso de HashTable causará problemas de eficiencia y el mismo bloqueo es demasiado ineficaz.
- Así que se introdujo ConcurrentHashMap y los bloqueos de segmento se usaban antes de jdk1.8. Los diferentes segmentos son bloqueos diferentes, pero incluso esto reducirá la eficiencia.
- Después de jdk1.8, ConcurrentHashMap introdujo un modo sin bloqueo.
- 4.1. Problemas existentes: en un entorno de subprocesos múltiples,
Explorar ConcurrentHashMap
- Bloqueo de segmento (antes 1.8)
- ConcurrentHashMap se compone de una estructura de matriz de segmentos y una estructura de matriz HashEntry. El segmento es un bloqueo reentrante ReentrantLock
- CAS + sincronizado (1.8)
- Principio de construcción inicial
//返回一个大于输入参数且最小的为2的n次幂的数。
private static final int tableSizeFor(int c) {
int n = c - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
//tableSizeFor(int c)的原理:
//将c最高位以下通过|=运算全部变成1,最后返回的时候,返回n+1;