tiempos de funcionamiento inesperados para el código HashSet

davidSC:

Así originalmente, tenía este código:

import java.util.*;

public class sandbox {
    public static void main(String[] args) {
        HashSet<Integer> hashSet = new HashSet<>();
        for (int i = 0; i < 100_000; i++) {
            hashSet.add(i);
        }

        long start = System.currentTimeMillis();

        for (int i = 0; i < 100_000; i++) {
            for (Integer val : hashSet) {
                if (val != -1) break;
            }

            hashSet.remove(i);
        }

        System.out.println("time: " + (System.currentTimeMillis() - start));
    }
}

Se tarda alrededor de 4s para ejecutar el bucles for anidados en mi equipo y yo no entiendo por qué se tardó tanto tiempo. El bucle exterior ejecuta 100.000 veces, el interior de bucle debe ejecutar 1 tiempo (debido a que cualquier valor de HashSet nunca será -1) y la eliminación de un elemento de un HashSet es O (1), por lo que debe ser alrededor de 200.000 operaciones. Si normalmente hay 100.000.000 de operaciones en un segundo, ¿cómo es que mi código toma 4s para correr?

Además, si la línea hashSet.remove(i);está comentada, el código sólo toma 16 ms. Si el interior de bucle esté comentada (pero no hashSet.remove(i);), el código sólo toma 8 ms.

apangin:

Usted ha creado un caso de uso marginal de HashSet, donde se degrada el algoritmo para la complejidad cuadrática.

Aquí está el bucle simplificada que lleva tanto tiempo:

for (int i = 0; i < 100_000; i++) {
    hashSet.iterator().next();
    hashSet.remove(i);
}

asíncrono de perfil muestra que casi todo el tiempo que se gasta en el interior java.util.HashMap$HashIterator()constructor:

    HashIterator() {
        expectedModCount = modCount;
        Node<K,V>[] t = table;
        current = next = null;
        index = 0;
        if (t != null && size > 0) { // advance to first entry
--->        do {} while (index < t.length && (next = t[index++]) == null);
        }
    }

La línea resaltada es un bucle lineal que las búsquedas de la primera cubeta no vacía en la tabla hash.

Desde Integertiene el trivial hashCode(es decir hashCode es igual al número en sí), resulta que los enteros consecutivos en su mayoría ocupan los cubos consecutivos en la tabla hash: número 0 va a la primera cubeta, número 1 va a la segunda cubeta, etc.

Ahora se quita los números consecutivos de 0 a 99999. En el caso más simple (cuando la cubeta contiene una sola tecla) la eliminación de una clave se implementa como anulando el elemento correspondiente en la matriz de cubo. Tenga en cuenta que la tabla no se compacta o un refrito después de la eliminación.

Así, los más teclas que borrar desde el principio de la matriz de cubo, los más largos HashIteratornecesidades para encontrar el primer cubo que no esté vacía.

Trate de eliminar las claves desde el otro extremo:

hashSet.remove(100_000 - i);

El algoritmo se convertirá en mucho más rápido!

Supongo que te gusta

Origin http://10.200.1.11:23101/article/api/json?id=424425&siteId=1
Recomendado
Clasificación