Explore HashMap y ConcurrentHashMap paso a paso

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:

  • Explore HashMap

    • ¿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
  • Explorar ConcurrentHashMap

    • 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.
// 找到第一个大于等于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
  • 4. [Problemas y soluciones de HashMap en el caso de multiproceso]

    • 4.1. Problemas existentes: en un entorno de subprocesos múltiples,
    • 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.

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;

Supongo que te gusta

Origin blog.csdn.net/ljfirst/article/details/107813985
Recomendado
Clasificación