La domanda è (ora) sulla memorizzazione di molti dati, che possono essere rappresentati usando tipi primitivi come int, in una Mappa. Alcune delle risposte qui sono molto fuorvianti secondo me. Vediamo perché.
Ho modificato il benchmark da trove a misurare sia il tempo di esecuzione che il consumo di memoria. Ho anche aggiunto PCJ a questo benchmark, che è un'altra libreria di raccolte per tipi primitivi (la uso ampiamente). Il benchmark "ufficiale" non confronta IntIntMaps con Java Collection Map<Integer, Integer>, probabilmente l'archiviazione Integerse l'archiviazione intsnon sono le stesse dal punto di vista tecnico. Ma a un utente potrebbe non interessare questo dettaglio tecnico, desidera archiviare i dati rappresentabili in modo intsefficiente.
Innanzitutto la parte pertinente del codice:
new Operation() {
private long usedMem() {
System.gc();
return Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory();
}
// trove
public void ours() {
long mem = usedMem();
TIntIntHashMap ours = new TIntIntHashMap(SET_SIZE);
for ( int i = dataset.size(); i-- > 0; ) {
ours.put(i, i);
}
mem = usedMem() - mem;
System.err.println("trove " + mem + " bytes");
ours.clear();
}
public void pcj() {
long mem = usedMem();
IntKeyIntMap map = new IntKeyIntOpenHashMap(SET_SIZE);
for ( int i = dataset.size(); i-- > 0; ) {
map.put(i, i);
}
mem = usedMem() - mem;
System.err.println("pcj " + mem + " bytes");
map.clear();
}
// java collections
public void theirs() {
long mem = usedMem();
Map<Integer, Integer> map = new HashMap<Integer, Integer>(SET_SIZE);
for ( int i = dataset.size(); i-- > 0; ) {
map.put(i, i);
}
mem = usedMem() - mem;
System.err.println("java " + mem + " bytes");
map.clear();
}
Presumo che i dati siano primitivi ints, il che sembra sano. Ma ciò implica una penalità di runtime per java util, a causa del boxing automatico, che non è necessario per i framework di raccolte primitive.
I risultati di runtime (senza gc()chiamate, ovviamente) su WinXP, jdk1.6.0_10:
100000 operazioni put 100000 contiene operazioni
raccolte java 1938 ms 203 ms
trove 234 ms 125 ms
pcj 516 ms 94 ms
Sebbene ciò possa già sembrare drastico, non è questo il motivo per utilizzare un tale framework.
Il motivo è la prestazione di memoria. I risultati per una mappa contenente 100000 intvoci:
Le raccolte java oscilla tra 6644536 e 7168840 byte
trove 1853296 byte
pcj 1866112 byte
Le raccolte Java richiedono una memoria tre volte superiore rispetto ai framework di raccolta primitivi. Vale a dire che è possibile conservare tre volte più dati in memoria, senza ricorrere all'IO del disco che riduce le prestazioni di runtime per grandezza. E questo conta. Leggi l'alta scalabilità per scoprire perché.
Nella mia esperienza, l'elevato consumo di memoria è il più grande problema di prestazioni con Java, che ovviamente comporta anche peggiori prestazioni di runtime. I framework di raccolta primitivi possono davvero aiutare qui.
Quindi: No, java.util non è la risposta. E "aggiungere funzionalità" alle raccolte Java non è il punto quando si chiede efficienza. Anche le moderne collezioni JDK non "superano nemmeno le collezioni specializzate Trove".
Disclaimer: il benchmark qui è tutt'altro che completo, né è perfetto. È pensato per portare a casa il punto, che ho sperimentato in molti progetti. Le raccolte primitive sono abbastanza utili per tollerare l'API fishy - se lavori con molti dati.