Jon Skeet ha recentemente sollevato un interessante argomento di programmazione sul suo blog: "C'è un buco nella mia astrazione, cara Liza, cara Liza" (enfasi aggiunta):
Ho un set - un
HashSet
, in effetti. Voglio rimuovere alcuni elementi da esso ... e molti di essi potrebbero non esistere. Infatti, nel nostro caso di prova, nessuno degli elementi nella raccolta "rimozioni" sarà nel set originale. Questo suona - e in effetti è - estremamente facile da codificare. Dopotutto, dobbiamoSet<T>.removeAll
aiutarci, giusto?Specifichiamo la dimensione del set "sorgente" e la dimensione della raccolta "rimozioni" sulla riga di comando, e li costruiamo entrambi. Il set di origine contiene solo numeri interi non negativi; il set di rimozioni contiene solo numeri interi negativi. Misuriamo quanto tempo ci vuole per rimuovere tutti gli elementi utilizzando
System.currentTimeMillis()
, che non è il cronometro più preciso al mondo ma è più che adeguato in questo caso, come vedrai. Ecco il codice: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"); } }
Cominciamo dandogli un lavoro facile: un set sorgente di 100 elementi e 100 da rimuovere:
c:UsersJonTest>java Test 100 100 Time taken: 1ms
Ok, quindi non ci aspettavamo che fosse lento ... chiaramente possiamo accelerare un po 'le cose. Che ne dici di una fonte di un milione di elementi e 300.000 elementi da rimuovere?
c:UsersJonTest>java Test 1000000 300000 Time taken: 38ms
Hmm. Sembra ancora abbastanza veloce. Ora sento di essere stato un po 'crudele, chiedendogli di fare tutta quella rimozione. Rendiamolo un po 'più semplice: 300.000 elementi di origine e 300.000 rimozioni:
c:UsersJonTest>java Test 300000 300000 Time taken: 178131ms
Mi scusi? Quasi tre minuti ? Yikes! Sicuramente dovrebbe essere più facile rimuovere oggetti da una raccolta più piccola di quella che abbiamo gestito in 38 ms?
Qualcuno può spiegare perché sta succedendo? Perché il HashSet<T>.removeAll
metodo è così lento?