La question est (maintenant) de stocker beaucoup de données, qui peuvent être représentées à l'aide de types primitifs comme int
, dans une carte. Certaines des réponses ici sont très trompeuses à mon avis. Voyons pourquoi.
J'ai modifié le benchmark de trove pour mesurer à la fois le temps d'exécution et la consommation de mémoire. J'ai également ajouté PCJ à ce benchmark, qui est une autre bibliothèque de collections pour les types primitifs (je l'utilise beaucoup). Le benchmark «officiel» de trove ne compare pas IntIntMaps à Java Collection Map<Integer, Integer>
, le stockage Integers
et le stockage ints
ne sont probablement pas les mêmes d'un point de vue technique. Mais un utilisateur peut ne pas se soucier de ce détail technique, il souhaite stocker des données représentables avec ints
efficacité.
D'abord la partie pertinente du code:
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();
}
Je suppose que les données sont primitives ints
, ce qui semble raisonnable. Mais cela implique une pénalité d'exécution pour java util, à cause de l'auto-boxing, qui n'est pas nécessaire pour les frameworks de collections primitives.
Les résultats d'exécution (sans gc()
appels, bien sûr) sur WinXP, jdk1.6.0_10:
100000 opérations put 100000 contient des opérations
collections java 1938 ms 203 ms
trove 234 ms 125 ms
pcj 516 ms 94 ms
Bien que cela puisse déjà sembler drastique, ce n'est pas la raison d'utiliser un tel cadre.
La raison est la performance de la mémoire. Les résultats pour une carte contenant 100000int
entrées:
les collections java oscille entre 6644536 et 7168840 octets
trove 1853296 octets
pcj 1866112 octets
Les collections Java ont besoin de plus de trois fois la mémoire par rapport aux frameworks de collection primitifs. C'est-à-dire que vous pouvez conserver trois fois plus de données en mémoire, sans recourir aux E / S de disque, ce qui réduit considérablement les performances d'exécution. Et cela compte. Lisez la haute évolutivité pour découvrir pourquoi.
D'après mon expérience, la consommation de mémoire élevée est le plus gros problème de performances avec Java, ce qui entraîne bien sûr également une dégradation des performances d'exécution. Les frameworks de collection primitifs peuvent vraiment aider ici.
Donc: Non, java.util n'est pas la réponse. Et «l'ajout de fonctionnalités» aux collections Java n'est pas le but lorsque l'on se questionne sur l'efficacité. De plus, les collections JDK modernes ne " surpassent pas même les collections spécialisées Trove".
Avertissement: le benchmark ici est loin d'être complet, ni parfait. Il vise à faire ressortir le point que j'ai vécu dans de nombreux projets. Les collections primitives sont suffisamment utiles pour tolérer une API louche - si vous travaillez avec beaucoup de données.