Java HashMap funciona

El proceso es generalmente similar a la HashMap usando lo siguiente:

HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("语文", 1);
map.put("数学", 2);
map.put("英语", 3);
map.put("历史", 4);
map.put("政治", 5);
map.put("地理", 6);
map.put("生物", 7);
map.put("化学", 8);
for(Entry<String, Integer> entry : map.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

¿Qué ha pasado? Aquí está una estructura general, esperamos tener una comprensión emocional de la estructura de HashMap:
Aquí Insertar imagen Descripción
documentación oficial se describe en el HashMap:

tabla hash implementación basada en la interfaz de mapa. Esta aplicación proporciona todas las operaciones de correlación opcional, y permite valores nulos y la tecla nula. (La clase HashMap es más o menos equivalente a Hashtable, excepto que es la falta de sincronización nulos y permisos.) Esta clase no ofrece ninguna garantía en cuanto a la orden del mapa; en particular, no garantiza que la orden permanecerá constante en el tiempo.

Dos parámetros importantes

Hay dos parámetros muy importantes, la capacidad (capacidad) y el factor de carga (factor de carga) en el HashMap:

  • La capacidad inicial de la capacidad es el número de cubos en la tabla hash, la capacidad inicial es simplemente la capacidad en el momento en que se crea la tabla hash.
  • El factor de carga de factor de carga es una medida de qué tan lleno está permitida la tabla hash para llegar antes de que su capacidad se incrementa automáticamente.

En pocas palabras, la capacidad es el número de cubos, el factor de carga es la relación entre el grado máximo de llenado cubos. Si iteración requisitos de alto rendimiento, la capacidad y luego no poner es demasiado alto, no se debe establecer el factor de carga demasiado bajo. Cuando el número de llenado número cubo (es decir, el número de elementos en el HashMap) Capacidad * factor de carga es mayor que la necesidad de ajustar para los cucharones actuales dos veces.

la función de venta

Función general idea expuesta:

  1. La clave para hashCode () hacer el hash, entonces el índice se calcula;
  2. Si no choque directamente en el cubo interior;
  3. Si la colisión, la presencia de los cubos en la forma de una lista enlazada;
  4. Si colisiones dan como resultado de cadena larga (mayor que o igual TREEIFY_THRESHOLD), convertida en una lista put árbol rojo-negro (1,8);
  5. Si el nodo ya existe en la sustitución de valor antiguo (garantizar la unicidad de la clave)
  6. Si el cubo está lleno (más del factor de carga * capacidad actual = umbral), se debe cambiar el tamaño.
public V put(K key, V value) {
    if (key == null)
        return putForNullKey(value);
    int hash = hash(key);
    int i = indexFor(hash, table.length);
    // 如果key已经存在,则替换value,并返回旧值
    for (Entry<K,V> e = table[i]; e != null; e = e.next) {
        Object k;
        if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
            V oldValue = e.value;
            e.value = value;
            e.recordAccess(this);
            return oldValue;
        }
    }

    modCount++;
    // key不存在,则插入新的元素
    addEntry(hash, key, value, i);
    return null;
}
función hash
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

Podemos ver esto es probablemente una función del papel: de 16 bits constante de alta, baja y alta de 16 bits 16 bits hizo un XOR. Donde los comentarios de código se escriben así:

Computes key.hashCode() and spreads (XORs) higher bits of hash to lower. Because the table uses power-of-two masking, sets of hashes that vary only in bits above the current mask will always collide. (Among known examples are sets of Float keys holding consecutive whole numbers in small tables.) So we apply a transform that spreads the impact of higher bits downward. There is a tradeoff between speed, utility, and quality of bit-spreading. Because many common sets of hashes are already reasonably distributed (so don’t benefit from spreading), and because we use trees to handle large sets of collisions in bins, we just XOR some shifted bits in the cheapest possible way to reduce systematic lossage, as well as to incorporate impact of the highest bits that would otherwise never be used in index calculations because of table bounds.

En el diseño de una función hash, como la tabla actual de longitud N es una potencia de 2, y calcula que se consigue el siguiente tiempo objetivo (utilizando y operaciones de bits en lugar de% resto):

(n - 1) & hash

Los diseñadores creen que este método es propenso a estrellarse. ¿Por qué dice? Puede que desee pensar en el n - 1 es 15 (0x1111), de hecho, el hash sólo tiene efecto poco significativo de baja de 4 bits, por supuesto, fácil estrelló.

Por lo tanto, el diseñador quería una forma de la situación general de la (teniendo en cuenta la velocidad, función, calidad), es el alto de 16 bits y 16 bits XOR mínima bits. Los diseñadores también explicaron que debido a que la mayor parte de la distribución de código hash ha sido muy buena, aunque también se chocó con O (log n) el árbol para hacerlo. O simplemente tener un aspecto diferente, no sólo reduce la sobrecarga del sistema, no lo hará porque la causa subyacente de la calculada inferior (longitud de la tabla es pequeña) no participó en alto nivel, causando la colisión.

Si todavía tenía colisiones frecuentes, lo que va a pasar? Nota del autor dijo que utilizan el árbol de manejar colisiones frecuentes (usamos árboles para manejar grandes conjuntos de colisiones en contenedores), en el PEC-180, una descripción del problema:

Mejorar el rendimiento de java.util.HashMap en condiciones de alta hash de colisión mediante el uso de árboles equilibrados en lugar de las listas enlazadas para almacenar entradas de mapa. Aplicar la misma mejora en la clase LinkedHashMap.

Se ha mencionado anteriormente, en conseguir elementos HashMap, el proceso básico de dos pasos:

  1. La primera hashCode () hacer el hash, a continuación, el cubo de índice determinado;
  2. Si el nodo clave de la cubeta no es lo que necesitamos, entonces () para encontrar en la cadena por keys.equals.

Se logra antes de las 8 resolución de conflictos de Java en una lista enlazada, en caso de una colisión, cuando consiga, de dos pasos de tiempo complejidad es O (1) + O (n). Por lo tanto, cuando la colisión es muy potente cuando n es grande, O (n) afecta claramente la velocidad de la velocidad.

Por lo tanto en Java 8, el uso de la lista de reemplazo árbol rojo-negro, por lo que la complejidad se convierte en O (1) + O (log n), y así, cuando un gran n, puede ser una solución ideal a este problema.

la función de cambio de tamaño y función de transferencia

Cuando se ponen, si encontramos el nivel de la cubeta actual de ocupación ha superado proporción factor de carga deseada, se cambiará el tamaño de producirse. En el curso de la de cambio de tamaño, simplemente significa que el cubo se expandió a 2 veces, y luego volver a calcular el índice, el nodo y a continuación, poner en una nueva cubeta. notas de cambio de tamaño describió de esta manera:

Inicializa o dobles de tamaño de tabla. Si nula, asigna en acuerdo con el objetivo de potencia inicial a cabo en el umbral de campo. De lo contrario, porque estamos utilizando expansión de potencias de dos, los elementos de cada bin deben permanecer ya sea en mismo índice, o movimiento con una potencia de dos desplazamiento en la nueva tabla.

lo que significa más o menos que, si se supera el límite será cambiar el tamaño, sino también porque estamos usando el poder dos veces extendida (en referencia a la longitud de la expansión del original 2 veces), por lo que la posición del elemento, ya sea en la posición original, ya sea después en la ubicación original posición móvil de la energía 2.

Por lo tanto, cuando expandimos HashMap no necesita volver a calcular el hash, sólo tenemos que mirar al nuevo valor de hash original que es 1 o 0 poco como, es 0, entonces el índice no ha cambiado, entonces el índice es 1 se convierte en " el índice original + oldCap".

Este diseño es muy inteligente en efecto, no sólo elimina la necesidad de tiempo para volver a calcular el valor hash, y al mismo tiempo, debido a la nueva 1 bit es 0 ó 1 se puede considerar al azar, redimensionar el proceso, incluso antes de que el conflicto descentralizado nodos a un nuevo cubo.

Compruebe la capacidad ha alcanzado el umbral de valor umbral:

void addEntry(int hash, K key, V value, int bucketIndex) {
    if ((size >= threshold) && (null != table[bucketIndex])) {
        resize(2 * table.length);
        hash = (null != key) ? hash(key) : 0;
        bucketIndex = indexFor(hash, table.length);
    }

    createEntry(hash, key, value, bucketIndex);
}

Expansión para lograr:

void resize(int newCapacity) {
    Entry[] oldTable = table;
    int oldCapacity = oldTable.length;
    ...

    Entry[] newTable = new Entry[newCapacity];
    ...
    transfer(newTable, rehash);
    table = newTable;
    threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
}

Aquí vamos a crear una matriz más grande, y por el método de transferencia, mover elementos:

void transfer(Entry[] newTable, boolean rehash) {
    int newCapacity = newTable.length;
    for (Entry<K,V> e : table) {
        while(null != e) {
            Entry<K,V> next = e.next;
            if (rehash) {
                e.hash = null == e.key ? 0 : hash(e.key);
            }
            int i = indexFor(e.hash, newCapacity);
            e.next = newTable[i];
            newTable[i] = e;
            e = next;
        }
    }
}
temas multi-hilo de seguridad

Estos son los nodos móviles lógica asociada.

Aquí Insertar imagen Descripción
Al insertar el cuarto nodo, la repetición se produce, suponemos que hay dos hilos simultáneamente, el hilo 1 y el hilo 2, dos hilos son nuevos la nueva matriz.

Aquí Insertar imagen Descripción
Rosca 2 se supone en la aplicación de la entrada <K, V> siguiente = e.next; después, segmento de tiempo cpu expira, el punto E variable de tiempo los nodos A, puntos hasta el siguiente nodo de la variable b.

Tema 1 continúa, es por desgracia, a, b, c nodo después de repetición se encuentra en la misma ubicación 7 para iniciar el nodo móvil.

El primer paso, el nodo móvil a.
Aquí Insertar imagen Descripción
Un segundo paso, el nodo móvil b.
Aquí Insertar imagen Descripción
Tenga en cuenta que, en donde el orden se invierte, el nodo móvil continúa c.
Aquí Insertar imagen Descripción
Esta vez rebanada hilo tiempo 1 se agota, el interior de la tabla no se ha establecido en la nueva Tablanueva, comienza la ejecución del hilo 2, la relación de referencia de tiempo interna es el siguiente:
Aquí Insertar imagen Descripción
En este caso, el hilo 2, el punto de la variable e nodos A, siguiente variable puntos al nodo B, la lógica restante comienzan el cuerpo del bucle.

Entry<K,V> next = e.next;
int i = indexFor(e.hash, newCapacity);
e.next = newTable[i];
newTable[i] = e;
e = next;

las relaciones de referencia en la siguiente figura después de la ejecución.
Aquí Insertar imagen Descripción
Después de la ejecución, el nodo de variable B al punto E, ya que e no es nulo, entonces el bucle continúa, después de ejecutarse la relación de referencia.
Aquí Insertar imagen Descripción
Después de la ejecución, el nodo de variable B al punto E, ya que e no es nulo, entonces el bucle continúa, después de ejecutarse la relación de referencia.
Aquí Insertar imagen Descripción
Variable e apunta de nuevo de vuelta al nodo a, sólo puede continuar con el bucle, donde el análisis cuidadoso en:

  1. Después de realizar la entrada <K, V> siguiente = e.next;, el nodo actual no es un lado, al lado que apunta a NULL variables;
  2. e.next = newtable [i]; en donde newtable [I] puntos al Nodo B, que es el siguiente punto a un nodo b, a y b entre sí de tal referencias, para formar un anillo;
  3. newtable [i] = e de la matriz del nodo en una posición i;
  4. e = próximo; asignar la variable e es nula, como un primer paso la variable es un puntero siguiente nulo;

Así que al final esto es una referencia a la relación.
Aquí Insertar imagen Descripción

resumen

Así, en el caso de los concurrentes, cuando se produce la expansión, puede producir lista circular, entrar a la aplicación, que dará lugar a un bucle infinito, causando 100% de la CPU problema, así que asegúrese de evitar el uso de HashMap en un entorno concurrente.

HashMap interpolación implementado analizada versión de la cola JDK1.8

Después de la HashMap JDK1.8 optimizado versiones, enteras las estructuras de datos en una lista en la forma de una matriz tal + + árbol rojo-negro. Y dijo cola cuando la interpolación es poner dentro de la posición de matriz elemento HashMap encima de la bañera en la lista o no el árbol rojo-negro, en este caso la nueva posición en el elemento de la lista para la cola de la lista, la interpolación de la cola llamada.

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
            boolean evict) {
    Node<K,V>[] tab; Node<K,V> p; int n, i;
    // 数组是否未初始化?若未初始化则进行初始化
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    // key的hash值经过位运算之后再和数组长度-1得到的值运算得到key在数组的下标
    // 若数组的这个位置还没有元素则直接将key-value放进去
    if ((p = tab[i = (n - 1) & hash]) == null)
        tab[i] = newNode(hash, key, value, null);
    else {
        // 若该下标位置已有元素
        Node<K,V> e; K k;
        // 是否已有元素的key值与新增元素的key判断是同一个
        if (p.hash == hash &&
            ((k = p.key) == key || (key != null && key.equals(k))))
            // 直接覆盖value
            e = p;
        // 如果已有元素是树节点
        else if (p instanceof TreeNode)
            // 将插入的元素新增为树节点
            e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
        else {
            // 不是树节点则只能是链表节点了,还未转化为树
            for (int binCount = 0; ; ++binCount) {
                if ((e = p.next) == null) {
                    // 遍历元素新增链表节点,此时可看该方法具体实现
                    p.next = newNode(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                        treeifyBin(tab, hash);
                    break;
                }
                if (e.hash == hash &&
                    ((k = e.key) == key || (key != null && key.equals(k))))
                    break;
                p = e;
            }
        }
        /**
        * 删除部分代码 
        */
    }
    ++modCount;
    if (++size > threshold)
        resize();
    afterNodeInsertion(evict);
    return null;
}

Después consulte p.next = newNode (hachís, clave, el valor nulo) que ponen en práctica un método particular, que se implementa el método de interpolación en el HashMap JDK1.8 cola:

Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) {
    return new Node<>(hash, key, value, next);
}

Node(int hash, K key, V value, Node<K,V> next) {
    this.hash = hash;
    this.key = key;
    this.value = value;
    this.next = next;
}

Dentro de este método, el nodo construye un nuevo nodo (el nuevo JDK1.8 implementación, heredado de entrada <K, V>), sus nodos sucesores describen la posición del punto más tarde nulo en la lista de elementos no lo es.

En la posterior p.next nodo = último elemento de la poligonal original de la lista para encontrar el newNode (hachís, clave, el valor nulo) Esta frase es a punto a la nueva configuración de nodo nodo. El efecto neto es en realidad un elemento nuevo se añade al final de la lista, que es HashMap en JDK1.8 interpolación cola.

Publicados 273 artículos originales · elogios ganado 13 · Vistas a 70000 +

Supongo que te gusta

Origin blog.csdn.net/LU_ZHAO/article/details/105137220
Recomendado
Clasificación