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>.removeAllayudarnos, ¿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: 1msBien, 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: 38msHmm. 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>.removeAllmétodo es tan lento?