Die Frage ist (jetzt), wie viele Daten, die mit primitiven Typen wie dargestellt werden können int
, in einer Karte gespeichert werden . Einige der Antworten hier sind meiner Meinung nach sehr irreführend. Mal sehen warum.
Ich habe den Benchmark von trove geändert , um sowohl die Laufzeit als auch den Speicherverbrauch zu messen. Ich habe diesem Benchmark auch PCJ hinzugefügt , eine weitere Sammlungsbibliothek für primitive Typen (ich verwende diese ausgiebig). Der "offizielle" Fundus-Benchmark vergleicht IntIntMaps nicht mit dem von Java Collection Map<Integer, Integer>
. Wahrscheinlich ist das Speichern Integers
und Speichern ints
aus technischer Sicht nicht dasselbe. Ein Benutzer interessiert sich jedoch möglicherweise nicht für dieses technische Detail. Er möchte Daten, mit denen er darstellbar ist, ints
effizient speichern.
Zuerst der relevante Teil des Codes:
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();
}
Ich gehe davon aus, dass die Daten primitiv sind ints
, was vernünftig erscheint. Dies impliziert jedoch eine Laufzeitstrafe für Java Util aufgrund des Auto-Boxing, das für die Frameworks für primitive Sammlungen nicht erforderlich ist.
Die Laufzeitergebnisse ( gc()
natürlich ohne Aufrufe) unter WinXP, jdk1.6.0_10:
100000 Put-Operationen 100000 enthält Operationen
Java-Sammlungen 1938 ms 203 ms
Fundgrube 234 ms 125 ms
pcj 516 ms 94 ms
Dies mag bereits drastisch erscheinen, ist jedoch nicht der Grund, ein solches Framework zu verwenden.
Der Grund ist die Speicherleistung. Die Ergebnisse für eine Karte mit 100000int
Einträgen:
Java-Sammlungen pendeln zwischen 6644536 und 7168840 Bytes
Fundus 1853296 Bytes
pcj 1866112 Bytes
Java-Sammlungen benötigen im Vergleich zu primitiven Sammlungsframeworks mehr als das Dreifache des Speichers. Das heißt, Sie können dreimal so viele Daten im Speicher behalten, ohne auf Festplatten-E / A zurückgreifen zu müssen, wodurch die Laufzeitleistung um Größenordnungen verringert wird. Und das ist wichtig. Lesen Sie Highscalability , um herauszufinden, warum.
Meiner Erfahrung nach ist ein hoher Speicherverbrauch das größte Leistungsproblem bei Java, was natürlich auch zu einer schlechteren Laufzeitleistung führt. Primitive Collection Frameworks können hier wirklich helfen.
Also: Nein, java.util ist nicht die Antwort. Und "Hinzufügen von Funktionen" zu Java-Sammlungen ist nicht der Punkt, wenn es um Effizienz geht. Auch die modernen JDK-Sammlungen übertreffen nicht einmal die spezialisierten Trove-Sammlungen.
Haftungsausschluss: Der Benchmark hier ist bei weitem nicht vollständig und auch nicht perfekt. Es soll den Punkt nach Hause fahren, den ich in vielen Projekten erlebt habe. Primitive Sammlungen sind nützlich genug, um fischartige APIs zu tolerieren - wenn Sie mit vielen Daten arbeiten.