Jon Skeet recientemente planteó un tema de programación interesante en su blog: "Hay un agujero en mi abstracción, querida Liza, querida Liza" (énfasis agregado):
Tengo un juego
HashSet
, de hecho. Quiero eliminar algunos elementos ... y es posible que muchos de los elementos no existan. De hecho, en nuestro caso de prueba, ninguno de los elementos de la colección "eliminaciones" estará en el conjunto original. Esto suena, y de hecho es , extremadamente fácil de codificar. Después de todo, tenemos queSet<T>.removeAll
ayudarnos, ¿verdad?Especificamos el tamaño del conjunto "fuente" y el tamaño de la colección de "eliminaciones" en la línea de comando, y construimos ambos. El conjunto fuente contiene solo enteros no negativos; el conjunto de eliminaciones contiene solo números enteros negativos. Medimos cuánto tiempo lleva eliminar todos los elementos usando
System.currentTimeMillis()
, que no es el cronómetro más preciso del mundo, pero es más que adecuado en este caso, como verás. Aquí está el código:import java.util.*; public class Test { public static void main(String[] args) { int sourceSize = Integer.parseInt(args[0]); int removalsSize = Integer.parseInt(args[1]); Set<Integer> source = new HashSet<Integer>(); Collection<Integer> removals = new ArrayList<Integer>(); for (int i = 0; i < sourceSize; i++) { source.add(i); } for (int i = 1; i <= removalsSize; i++) { removals.add(-i); } long start = System.currentTimeMillis(); source.removeAll(removals); long end = System.currentTimeMillis(); System.out.println("Time taken: " + (end - start) + "ms"); } }
Comencemos por darle un trabajo fácil: un conjunto de fuentes de 100 elementos y 100 para eliminar:
c:UsersJonTest>java Test 100 100 Time taken: 1ms
Bien, no esperábamos que fuera lento… claramente podemos acelerar un poco las cosas. ¿Qué tal una fuente de un millón de elementos y 300.000 elementos para eliminar?
c:UsersJonTest>java Test 1000000 300000 Time taken: 38ms
Hmm. Eso todavía parece bastante rápido. Ahora siento que he sido un poco cruel, pidiéndole que haga todo eso. Hagámoslo un poco más fácil: 300.000 elementos de origen y 300.000 eliminaciones:
c:UsersJonTest>java Test 300000 300000 Time taken: 178131ms
¿Perdóneme? ¿Casi tres minutos ? ¡Ay! Seguramente debería ser más fácil eliminar elementos de una colección más pequeña que la que manejamos en 38ms.
¿Alguien puede explicar por qué está sucediendo esto? ¿Por qué el HashSet<T>.removeAll
método es tan lento?