En Java, il existe un grand nombre de méthodes qui ont toutes pour but de manipuler des chaînes de caractères. L'exemple le plus simple est la méthode String.split("quelque chose").
La définition réelle d'un grand nombre de ces méthodes est qu'elles prennent toutes une expression régulière comme paramètre(s) d'entrée. Ce qui en fait des blocs de construction très puissants.
Maintenant, il y a deux effets que vous verrez dans beaucoup de ces méthodes :
- Ils recompilent l'expression chaque fois que la méthode est invoquée. Elles ont donc un impact sur les performances.
- J'ai constaté que dans la plupart des situations "réelles", ces méthodes sont appelées avec des textes "fixes". L'utilisation la plus courante de la méthode split est encore pire : elle est généralement appelée avec un seul caractère (généralement un ' ', un ';' ou un '&') à séparer.
Ainsi, les méthodes par défaut ne sont pas seulement puissantes, elles semblent également surpuissantes pour ce à quoi elles sont réellement utilisées. En interne, nous avons développé une méthode "fastSplit" qui sépare les chaînes de caractères fixes. J'ai écrit un test à la maison pour voir combien plus vite je pourrais le faire si l'on savait qu'il s'agissait d'un seul caractère. Les deux méthodes sont nettement plus rapides que la méthode de fractionnement "standard".
Je me demandais donc : pourquoi l'API Java a été choisie telle qu'elle est maintenant ? Quelle était la bonne raison d'opter pour cela au lieu d'avoir quelque chose comme split(char), split(String) et splitRegex(String) ?
Mise à jour : J'ai fait quelques appels pour voir combien de temps prennent les différentes façons de diviser une chaîne.
Bref résumé : il fait un grand différence !
J'ai fait 10000000 itérations pour chaque cas de test, en utilisant toujours l'entrée
"aap,noot,mies,wim,zus,jet,teun"
et en utilisant toujours ',' ou "," comme argument de division.
Voici ce que j'ai obtenu sur mon système Linux (c'est une boîte Atom D510, donc un peu lente) :
fastSplit STRING
Test 1 : 11405 milliseconds: Split in several pieces
Test 2 : 3018 milliseconds: Split in 2 pieces
Test 3 : 4396 milliseconds: Split in 3 pieces
homegrown fast splitter based on char
Test 4 : 9076 milliseconds: Split in several pieces
Test 5 : 2024 milliseconds: Split in 2 pieces
Test 6 : 2924 milliseconds: Split in 3 pieces
homegrown splitter based on char that always splits in 2 pieces
Test 7 : 1230 milliseconds: Split in 2 pieces
String.split(regex)
Test 8 : 32913 milliseconds: Split in several pieces
Test 9 : 30072 milliseconds: Split in 2 pieces
Test 10 : 31278 milliseconds: Split in 3 pieces
String.split(regex) using precompiled Pattern
Test 11 : 26138 milliseconds: Split in several pieces
Test 12 : 23612 milliseconds: Split in 2 pieces
Test 13 : 24654 milliseconds: Split in 3 pieces
StringTokenizer
Test 14 : 27616 milliseconds: Split in several pieces
Test 15 : 28121 milliseconds: Split in 2 pieces
Test 16 : 27739 milliseconds: Split in 3 pieces
Comme vous pouvez le constater, cela fait une grande différence si vous avez beaucoup de fractionnements à faire.
Pour vous donner un aperçu, je suis actuellement dans l'arène des fichiers journaux Apache et d'Hadoop avec les données d'un projet de recherche sur la santé. grand site web. Donc, pour moi, ce genre de choses compte vraiment :)
Un élément que je n'ai pas pris en compte ici est le ramasseur de déchets. D'après ce que je sais, la compilation d'une expression régulière dans un Pattern/Matcher/... allouera beaucoup d'objets, qui devront être collectés à un moment donné. Donc peut-être qu'à long terme, les différences entre ces versions sont encore plus grandes .... ou plus petites.
Mes conclusions jusqu'à présent :
- Ne l'optimisez que si vous avez BEAUCOUP de chaînes à diviser.
- Si vous utilisez les méthodes regex, précompilez toujours si vous utilisez plusieurs fois le même motif.
- Oubliez le StringTokenizer (obsolète)
- Si vous voulez diviser un seul caractère, utilisez une méthode personnalisée, surtout si vous avez besoin de le diviser en un nombre spécifique de morceaux (comme ... 2).
P.S. Je vous donne toutes mes méthodes de division par char pour jouer avec (sous la licence que tout sur ce site tombe sous :) ). Je ne les ai jamais testées complètement pour le moment. Amusez-vous bien.
private static String[]
stringSplitChar(final String input,
final char separator) {
int pieces = 0;
// First we count how many pieces we will need to store ( = separators + 1 )
int position = 0;
do {
pieces++;
position = input.indexOf(separator, position + 1);
} while (position != -1);
// Then we allocate memory
final String[] result = new String[pieces];
// And start cutting and copying the pieces.
int previousposition = 0;
int currentposition = input.indexOf(separator);
int piece = 0;
final int lastpiece = pieces - 1;
while (piece < lastpiece) {
result[piece++] = input.substring(previousposition, currentposition);
previousposition = currentposition + 1;
currentposition = input.indexOf(separator, previousposition);
}
result[piece] = input.substring(previousposition);
return result;
}
private static String[]
stringSplitChar(final String input,
final char separator,
final int maxpieces) {
if (maxpieces <= 0) {
return stringSplitChar(input, separator);
}
int pieces = maxpieces;
// Then we allocate memory
final String[] result = new String[pieces];
// And start cutting and copying the pieces.
int previousposition = 0;
int currentposition = input.indexOf(separator);
int piece = 0;
final int lastpiece = pieces - 1;
while (currentposition != -1 && piece < lastpiece) {
result[piece++] = input.substring(previousposition, currentposition);
previousposition = currentposition + 1;
currentposition = input.indexOf(separator, previousposition);
}
result[piece] = input.substring(previousposition);
// All remaining array elements are uninitialized and assumed to be null
return result;
}
private static String[]
stringChop(final String input,
final char separator) {
String[] result;
// Find the separator.
final int separatorIndex = input.indexOf(separator);
if (separatorIndex == -1) {
result = new String[1];
result[0] = input;
}
else {
result = new String[2];
result[0] = input.substring(0, separatorIndex);
result[1] = input.substring(separatorIndex + 1);
}
return result;
}