Einige Testergebnisse
Ich habe viele gute Antworten auf diese Frage erhalten - danke Leute -, also habe ich beschlossen, einige Tests durchzuführen und herauszufinden, welche Methode tatsächlich am schnellsten ist. Die fünf Methoden, die ich getestet habe, sind folgende:
- die "ContainsKey" -Methode, die ich in der Frage vorgestellt habe
- die von Aleksandar Dimitrov vorgeschlagene "TestForNull" -Methode
- die von Hank Gay vorgeschlagene "AtomicLong" -Methode
- die von jrudolph vorgeschlagene "Trove" -Methode
- die von phax.myopenid.com vorgeschlagene "MutableInt" -Methode
Methode
Folgendes habe ich getan ...
- hat fünf Klassen erstellt, die bis auf die unten gezeigten Unterschiede identisch waren. Jede Klasse musste eine für das von mir vorgestellte Szenario typische Operation ausführen: Öffnen einer 10-MB-Datei und Einlesen sowie anschließende Häufigkeitszählung aller Wort-Token in der Datei. Da dies durchschnittlich nur 3 Sekunden dauerte, ließ ich die Frequenzzählung (nicht die E / A) 10 Mal durchführen.
- Die Schleife von 10 Iterationen, jedoch nicht die E / A-Operation, wurde zeitlich festgelegt und die Gesamtzeit (in Uhrsekunden) im Wesentlichen nach Ian Darwins Methode im Java-Kochbuch aufgezeichnet .
- führte alle fünf Tests in Serie durch und tat dies dann noch dreimal.
- gemittelt die vier Ergebnisse für jede Methode.
Ergebnisse
Ich werde zuerst die Ergebnisse und den folgenden Code für diejenigen präsentieren, die interessiert sind.
Die ContainsKey- Methode war erwartungsgemäß die langsamste, daher gebe ich die Geschwindigkeit jeder Methode im Vergleich zur Geschwindigkeit dieser Methode an.
- Enthält Schlüssel : 30,654 Sekunden (Grundlinie)
- AtomicLong: 29,780 Sekunden (1,03-mal so schnell)
- TestForNull: 28,804 Sekunden (1,06-mal so schnell)
- Fundgrube : 26,313 Sekunden (1,16-mal so schnell)
- MutableInt: 25,747 Sekunden (1,19-mal so schnell)
Schlussfolgerungen
Es scheint, dass nur die MutableInt-Methode und die Trove-Methode wesentlich schneller sind, da nur sie eine Leistungssteigerung von mehr als 10% bewirken. Wenn Threading jedoch ein Problem darstellt, ist AtomicLong möglicherweise attraktiver als die anderen (ich bin mir nicht sicher). Ich habe auch TestForNull mit final
Variablen ausgeführt, aber der Unterschied war vernachlässigbar.
Beachten Sie, dass ich die Speichernutzung in den verschiedenen Szenarien nicht profiliert habe. Ich würde mich freuen, von jedem zu hören, der gute Einblicke in die Auswirkungen der Methoden MutableInt und Trove auf die Speichernutzung hat.
Persönlich finde ich die MutableInt-Methode am attraktivsten, da keine Klassen von Drittanbietern geladen werden müssen. Wenn ich also keine Probleme damit entdecke, gehe ich wahrscheinlich so vor.
Der Code
Hier ist der entscheidende Code für jede Methode.
Enthält Schlüssel
import java.util.HashMap;
import java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
int count = freq.containsKey(word) ? freq.get(word) : 0;
freq.put(word, count + 1);
TestForNull
import java.util.HashMap;
import java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
Integer count = freq.get(word);
if (count == null) {
freq.put(word, 1);
}
else {
freq.put(word, count + 1);
}
AtomicLong
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
...
final ConcurrentMap<String, AtomicLong> map =
new ConcurrentHashMap<String, AtomicLong>();
...
map.putIfAbsent(word, new AtomicLong(0));
map.get(word).incrementAndGet();
Fundgrube
import gnu.trove.TObjectIntHashMap;
...
TObjectIntHashMap<String> freq = new TObjectIntHashMap<String>();
...
freq.adjustOrPutValue(word, 1, 1);
MutableInt
import java.util.HashMap;
import java.util.Map;
...
class MutableInt {
int value = 1; // note that we start at 1 since we're counting
public void increment () { ++value; }
public int get () { return value; }
}
...
Map<String, MutableInt> freq = new HashMap<String, MutableInt>();
...
MutableInt count = freq.get(word);
if (count == null) {
freq.put(word, new MutableInt());
}
else {
count.increment();
}