Dans mon logiciel, j'ai besoin de diviser une chaîne en mots. J'ai actuellement plus de 19 000 000 de documents avec plus de 30 mots chacun.
Laquelle des deux manières suivantes est la meilleure façon de procéder (en termes de performances)?
StringTokenizer sTokenize = new StringTokenizer(s," ");
while (sTokenize.hasMoreTokens()) {
ou
String[] splitS = s.split(" ");
for(int i =0; i < splitS.length; i++)
Si vos données se trouvent déjà dans une base de données, vous devez analyser la chaîne de mots, je vous suggérons d'utiliser indexOf à plusieurs reprises. C'est plusieurs fois plus rapide que l'une ou l'autre solution.
Cependant, obtenir les données d'une base de données est encore beaucoup plus coûteux.
StringBuilder sb = new StringBuilder();
for (int i = 100000; i < 100000 + 60; i++)
sb.append(i).append(' ');
String sample = sb.toString();
int runs = 100000;
for (int i = 0; i < 5; i++) {
{
long start = System.nanoTime();
for (int r = 0; r < runs; r++) {
StringTokenizer st = new StringTokenizer(sample);
List<String> list = new ArrayList<String>();
while (st.hasMoreTokens())
list.add(st.nextToken());
}
long time = System.nanoTime() - start;
System.out.printf("StringTokenizer took an average of %.1f us%n", time / runs / 1000.0);
}
{
long start = System.nanoTime();
Pattern spacePattern = Pattern.compile(" ");
for (int r = 0; r < runs; r++) {
List<String> list = Arrays.asList(spacePattern.split(sample, 0));
}
long time = System.nanoTime() - start;
System.out.printf("Pattern.split took an average of %.1f us%n", time / runs / 1000.0);
}
{
long start = System.nanoTime();
for (int r = 0; r < runs; r++) {
List<String> list = new ArrayList<String>();
int pos = 0, end;
while ((end = sample.indexOf(' ', pos)) >= 0) {
list.add(sample.substring(pos, end));
pos = end + 1;
}
}
long time = System.nanoTime() - start;
System.out.printf("indexOf loop took an average of %.1f us%n", time / runs / 1000.0);
}
}
empreintes
StringTokenizer took an average of 5.8 us
Pattern.split took an average of 4.8 us
indexOf loop took an average of 1.8 us
StringTokenizer took an average of 4.9 us
Pattern.split took an average of 3.7 us
indexOf loop took an average of 1.7 us
StringTokenizer took an average of 5.2 us
Pattern.split took an average of 3.9 us
indexOf loop took an average of 1.8 us
StringTokenizer took an average of 5.1 us
Pattern.split took an average of 4.1 us
indexOf loop took an average of 1.6 us
StringTokenizer took an average of 5.0 us
Pattern.split took an average of 3.8 us
indexOf loop took an average of 1.6 us
Le coût d'ouverture d'un fichier sera d'environ 8 ms. Comme les fichiers sont si petits, votre cache peut améliorer les performances d'un facteur 2 à 5 fois supérieur. Malgré tout, sa passera environ 10 heures à ouvrir des fichiers. Le coût d'utilisation de split vs StringTokenizer est nettement inférieur à 0,01 ms chacun. Pour analyser 19 millions de x 30 mots * 8 lettres par mot devraient prendre environ 10 secondes (à environ 1 Go toutes les 2 secondes)
Si vous souhaitez améliorer les performances, je vous suggère d’avoir beaucoup moins de fichiers. par exemple. utiliser une base de données. Si vous ne voulez pas utiliser une base de données SQL, je suggère d'utiliser l'un de ces http://nosql-database.org/
Split en Java 7 appelle simplement indexOf pour cette entrée, voir la source . La scission devrait être très rapide, proche d'appels répétés d'indexOf.
La spécification de l'API Java recommande d'utiliser split
. Voir la documentation de StringTokenizer
.
Une autre chose importante, non documentée à ce que je sache, est que demander à StringTokenizer de renvoyer les délimiteurs avec la chaîne à jeton (en utilisant le constructeur StringTokenizer(String str, String delim, boolean returnDelims)
) réduit également le temps de traitement. Donc, si vous recherchez de la performance, je vous recommanderais d'utiliser quelque chose comme:
private static final String DELIM = "#";
public void splitIt(String input) {
StringTokenizer st = new StringTokenizer(input, DELIM, true);
while (st.hasMoreTokens()) {
String next = getNext(st);
System.out.println(next);
}
}
private String getNext(StringTokenizer st){
String value = st.nextToken();
if (DELIM.equals(value))
value = null;
else if (st.hasMoreTokens())
st.nextToken();
return value;
}
Malgré les frais généraux introduits par la méthode getNext (), qui supprime les délimiteurs pour vous, cela reste 50% plus rapide selon mes critères.
Utilisez split.
StringTokenizer est une classe héritée conservée pour des raisons de compatibilité, bien que son utilisation soit déconseillée dans le nouveau code. Il est recommandé à ceux qui recherchent cette fonctionnalité d’utiliser la méthode split à la place.
Quel que soit son statut, je pense que StringTokenizer
sera beaucoup plus rapide que String.split()
pour cette tâche, car elle n’utilise pas d’expressions régulières: elle analyse directement l’entrée, comme vous le feriez vous-même via indexOf()
. En fait, String.split()
doit compiler la regex à chaque fois que vous l'appelez, aussi n'est-il pas aussi efficace que d'utiliser directement une expression régulière vous-même.
Que doivent faire les 19 000 000 de documents? Devez-vous fractionner les mots dans tous les documents régulièrement? Ou est-ce un problème d'un coup?
Si vous affichez/demandez un document à la fois, avec seulement 30 Word, il s’agit d’un problème tellement minime que toute méthode fonctionnerait.
Si vous devez traiter tous les documents à la fois, avec seulement 30 mots, il s'agit d'un problème si minime qu'il est plus probable que vous soyez lié à IO.
Lorsque vous exécutez des tests de performance micro (et dans ce cas, même nano), de nombreux facteurs affectent vos résultats. Les optimisations JIT et la récupération de place pour n'en nommer que quelques-unes.
Pour obtenir des résultats significatifs à partir des micro-points de référence, consultez la bibliothèque jmh . Il contient d’excellents exemples sur la manière d’exécuter de bons tests de performance.
Performance sage StringTokeniser est bien meilleur que split. Vérifiez le code ci-dessous,
Mais, selon la documentation Java, son utilisation est déconseillée. Vérifier ici
Cela pourrait être une comparaison raisonnable en utilisant 1.6.0
http://www.javamex.com/tutorials/regular_expressions/splitting_tokenisation_performance.shtml#.V6-CZvnhCM8