Quelques résultats de tests
J'ai reçu beaucoup de bonnes réponses à cette question - merci à tous - et j'ai donc décidé de faire quelques tests pour déterminer la méthode la plus rapide. Les cinq méthodes que j'ai testées sont les suivantes :
- la méthode "ContainsKey" que j'ai présentée dans la question
- la méthode "TestForNull" suggérée par Aleksandar Dimitrov
- la méthode "AtomicLong" suggérée par Hank Gay
- la méthode "Trove" suggérée par jrudolph
- la méthode "MutableInt" proposée par phax.myopenid.com
Méthode
Voilà ce que j'ai fait...
- a créé cinq classes identiques, à l'exception des différences indiquées ci-dessous. Chaque classe devait effectuer une opération typique du scénario que j'ai présenté : ouvrir un fichier de 10 Mo et le lire, puis effectuer un comptage de fréquence de tous les tokens de mots dans le fichier. Comme cette opération ne prenait en moyenne que 3 secondes, j'ai demandé à la classe d'effectuer le comptage de fréquence (et non les entrées/sorties) 10 fois.
- a chronométré la boucle de 10 itérations mais pas l'opération I/O et a enregistré le temps total pris (en secondes d'horloge) en utilisant essentiellement La méthode de Ian Darwin dans le Java Cookbook .
- a effectué les cinq tests en série, puis l'a refait trois fois.
- a fait la moyenne des quatre résultats pour chaque méthode.
Résultats
Je vais présenter les résultats en premier et le code ci-dessous pour ceux qui sont intéressés.
El ContainsKey était, comme prévu, la plus lente, je vais donc donner la vitesse de chaque méthode par rapport à celle de cette dernière.
-
ContainsKey : 30,654 secondes (ligne de base)
-
AtomicLong : 29,780 secondes (1,03 fois plus rapide)
-
TestForNull : 28,804 secondes (1,06 fois plus rapide)
-
Trove : 26,313 secondes (1,16 fois plus rapide)
-
MutableInt : 25,747 secondes (1,19 fois plus rapide)
Conclusions
Il semblerait que seules la méthode MutableInt et la méthode Trove soient significativement plus rapides, dans la mesure où elles sont les seules à offrir un gain de performance de plus de 10%. Cependant, si le threading est un problème, AtomicLong pourrait être plus intéressant que les autres (je ne suis pas vraiment sûr). J'ai également exécuté TestForNull avec final
mais la différence est négligeable.
Notez que je n'ai pas établi de profil d'utilisation de la mémoire dans les différents scénarios. Je serais heureux d'entendre toute personne ayant une idée précise de la façon dont les méthodes MutableInt et Trove sont susceptibles d'affecter l'utilisation de la mémoire.
Personnellement, je trouve la méthode MutableInt la plus intéressante, car elle ne nécessite pas le chargement de classes tierces. Donc, à moins que je ne découvre des problèmes avec cette méthode, c'est celle que je choisirai le plus probablement.
Le code
Voici le code crucial de chaque méthode.
ContainsKey
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();
Trove
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();
}
0 votes
Je pense que ce serait la même chose pour java.util.Hashtable.
3 votes
Bien sûr, ce serait la même chose, car Hashtable est en fait une carte.
2 votes
Java 8 : exemple de computeIfAbsent : stackoverflow.com/a/37439971/1216775
2 votes
Jetez un coup d'œil à
Map::computeIfAbsent
et des amis.