Existe-t-il une différence perceptible entre l'utilisation de String.format
et la concaténation de chaînes en Java?
J'ai tendance à utiliser String.format
mais parfois je glisse et utilise une concaténation. Je me demandais si l'un était meilleur que l'autre.
D'après ce que je vois, String.format
vous donne plus de pouvoir pour "formater" la chaîne; et la concaténation signifie que vous n’aurez plus à craindre d’introduire accidentellement un% s supplémentaire ou d’en manquer un.
String.format
est également plus court.
Lequel est le plus lisible dépend de la façon dont votre tête fonctionne.
Je suggérerais qu'il est préférable d'utiliser String.format()
. La raison principale est que String.format()
peut être plus facilement localisé avec le texte chargé à partir de fichiers de ressources, tandis que la concaténation ne peut pas être localisée sans produire un nouvel exécutable avec un code différent pour chaque langue.
Si vous envisagez de localiser votre application, vous devez également prendre l'habitude de spécifier également la position des arguments pour vos jetons de format:
"Hello %1$s the time is %2$t"
Cela peut ensuite être localisé et avoir les jetons de nom et d'heure échangés sans nécessiter une recompilation de l'exécutable pour prendre en compte les différents ordres. Avec les positions d'argument, vous pouvez également réutiliser le même argument sans le transmettre deux fois à la fonction:
String.format("Hello %1$s, your name is %1$s and the time is %2$t", name, time)
A propos de la performance:
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
for(int i = 0; i < 1000000; i++){
String s = "Hi " + i + "; Hi to you " + i*2;
}
long end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;
start = System.currentTimeMillis();
for(int i = 0; i < 1000000; i++){
String s = String.format("Hi %s; Hi to you %s",i, + i*2);
}
end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
}
Les résultats de chronométrage sont les suivants:
Par conséquent, la concaténation est beaucoup plus rapide que String.format.
Puisqu'il y a une discussion sur les performances, j'ai décidé d'ajouter une comparaison incluant StringBuilder. En fait, il est plus rapide que concat et, naturellement, l’option String.format.
Pour en faire une sorte de comparaison pomme à pomme, j’instancie un nouveau StringBuilder dans la boucle plutôt qu’à l’extérieur (c’est en fait plus rapide que de faire une seule instanciation, probablement à cause de la surcharge de la réallocation d’espace pour la boucle ajoutée à la fin de un constructeur).
String formatString = "Hi %s; Hi to you %s";
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format(formatString, i, +i * 2);
}
long end = System.currentTimeMillis();
log.info("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = "Hi " + i + "; Hi to you " + i * 2;
}
end = System.currentTimeMillis();
log.info("Concatenation = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
StringBuilder bldString = new StringBuilder("Hi ");
bldString.append(i).append("; Hi to you ").append(i * 2);
}
end = System.currentTimeMillis();
log.info("String Builder = " + ((end - start)) + " millisecond");
Un problème avec .format
est que vous perdez la sécurité de type statique. Vous pouvez avoir trop peu d'arguments pour votre format et les types incorrects pour les spécificateurs de format - les deux conduisant à un IllegalFormatException
au moment de l'exécution, de sorte que vous pourriez vous retrouver avec un code de journalisation qui interrompt la production.
En revanche, les arguments de +
peuvent être testés par le compilateur.
Lequel est le plus lisible dépend de la façon dont votre tête fonctionne.
Vous avez votre réponse ici.
C'est une question de goût personnel.
La concaténation de chaînes est légèrement plus rapide, je suppose, mais cela devrait être négligeable.
Voici un test avec plusieurs tailles d'échantillon en millisecondes.
public class Time {
public static String sysFile = "/sys/class/camera/rear/rear_flash";
public static String cmdString = "echo %s > " + sysFile;
public static void main(String[] args) {
int i = 1;
for(int run=1; run <= 12; run++){
for(int test =1; test <= 2 ; test++){
System.out.println(
String.format("\nTEST: %s, RUN: %s, Iterations: %s",run,test,i));
test(run, i);
}
System.out.println("\n____________________________");
i = i*3;
}
}
public static void test(int run, int iterations){
long start = System.nanoTime();
for( int i=0;i<iterations; i++){
String s = "echo " + i + " > "+ sysFile;
}
long t = System.nanoTime() - start;
String r = String.format(" %-13s =%10d %s", "Concatenation",t,"nanosecond");
System.out.println(r) ;
start = System.nanoTime();
for( int i=0;i<iterations; i++){
String s = String.format(cmdString, i);
}
t = System.nanoTime() - start;
r = String.format(" %-13s =%10d %s", "Format",t,"nanosecond");
System.out.println(r);
start = System.nanoTime();
for( int i=0;i<iterations; i++){
StringBuilder b = new StringBuilder("echo ");
b.append(i).append(" > ").append(sysFile);
String s = b.toString();
}
t = System.nanoTime() - start;
r = String.format(" %-13s =%10d %s", "StringBuilder",t,"nanosecond");
System.out.println(r);
}
}
TEST: 1, RUN: 1, Iterations: 1
Concatenation = 14911 nanosecond
Format = 45026 nanosecond
StringBuilder = 3509 nanosecond
TEST: 1, RUN: 2, Iterations: 1
Concatenation = 3509 nanosecond
Format = 38594 nanosecond
StringBuilder = 3509 nanosecond
____________________________
TEST: 2, RUN: 1, Iterations: 3
Concatenation = 8479 nanosecond
Format = 94438 nanosecond
StringBuilder = 5263 nanosecond
TEST: 2, RUN: 2, Iterations: 3
Concatenation = 4970 nanosecond
Format = 92976 nanosecond
StringBuilder = 5848 nanosecond
____________________________
TEST: 3, RUN: 1, Iterations: 9
Concatenation = 11403 nanosecond
Format = 287115 nanosecond
StringBuilder = 14326 nanosecond
TEST: 3, RUN: 2, Iterations: 9
Concatenation = 12280 nanosecond
Format = 209051 nanosecond
StringBuilder = 11818 nanosecond
____________________________
TEST: 5, RUN: 1, Iterations: 81
Concatenation = 54383 nanosecond
Format = 1503113 nanosecond
StringBuilder = 40056 nanosecond
TEST: 5, RUN: 2, Iterations: 81
Concatenation = 44149 nanosecond
Format = 1264241 nanosecond
StringBuilder = 34208 nanosecond
____________________________
TEST: 6, RUN: 1, Iterations: 243
Concatenation = 76018 nanosecond
Format = 3210891 nanosecond
StringBuilder = 76603 nanosecond
TEST: 6, RUN: 2, Iterations: 243
Concatenation = 91222 nanosecond
Format = 2716773 nanosecond
StringBuilder = 73972 nanosecond
____________________________
TEST: 8, RUN: 1, Iterations: 2187
Concatenation = 527450 nanosecond
Format = 10291108 nanosecond
StringBuilder = 885027 nanosecond
TEST: 8, RUN: 2, Iterations: 2187
Concatenation = 526865 nanosecond
Format = 6294307 nanosecond
StringBuilder = 591773 nanosecond
____________________________
TEST: 10, RUN: 1, Iterations: 19683
Concatenation = 4592961 nanosecond
Format = 60114307 nanosecond
StringBuilder = 2129387 nanosecond
TEST: 10, RUN: 2, Iterations: 19683
Concatenation = 1850166 nanosecond
Format = 35940524 nanosecond
StringBuilder = 1885544 nanosecond
____________________________
TEST: 12, RUN: 1, Iterations: 177147
Concatenation = 26847286 nanosecond
Format = 126332877 nanosecond
StringBuilder = 17578914 nanosecond
TEST: 12, RUN: 2, Iterations: 177147
Concatenation = 24405056 nanosecond
Format = 129707207 nanosecond
StringBuilder = 12253840 nanosecond
Voici le même test que ci-dessus avec la modification de l'appel de la méthode toString () sur la StringBuilder . Les résultats ci-dessous montrent que l'approche StringBuilder est juste un peu plus lente que la concaténation de chaînes utilisant l'opérateur +.
fichier: StringTest.Java
class StringTest {
public static void main(String[] args) {
String formatString = "Hi %s; Hi to you %s";
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format(formatString, i, +i * 2);
}
long end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = "Hi " + i + "; Hi to you " + i * 2;
}
end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
StringBuilder bldString = new StringBuilder("Hi ");
bldString.append(i).append("Hi to you ").append(i * 2).toString();
}
end = System.currentTimeMillis();
System.out.println("String Builder = " + ((end - start)) + " millisecond");
}
}
Commandes Shell: (compiler et exécuter StringTest 5 fois)
> javac StringTest.Java
> sh -c "for i in \$(seq 1 5); do echo \"Run \${i}\"; Java StringTest; done"
Résultats:
Run 1
Format = 1290 millisecond
Concatenation = 115 millisecond
String Builder = 130 millisecond
Run 2
Format = 1265 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond
Run 3
Format = 1303 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond
Run 4
Format = 1297 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond
Run 5
Format = 1270 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond
String.format()
est plus qu'une simple concaténation de chaînes. Par exemple, vous pouvez afficher des nombres dans des paramètres régionaux spécifiques en utilisant String.format()
.
Cependant, si vous ne vous souciez pas de la localisation, il n'y a pas de différence fonctionnelle. Peut-être que l'un est plus rapide que l'autre, mais dans la plupart des cas, ce sera négligeable.
Généralement, la concaténation de chaînes est préférable à String.format
. Ce dernier a deux inconvénients principaux:
Par le point 1, je veux dire qu’il n’est pas possible de comprendre ce que fait un appel String.format()
en un seul passage séquentiel. On est obligé de faire l'aller-retour entre la chaîne de format et les arguments, en comptant la position des arguments. Pour les concaténations courtes, ce n’est pas vraiment un problème. Dans ces cas, toutefois, la concaténation de chaînes est moins détaillée.
Par le point 2, je veux dire que la partie importante du processus de construction est codée dans le chaîne de format (en utilisant un DSL). L'utilisation de chaînes pour représenter le code présente de nombreux inconvénients. Il n'est pas intrinsèquement sûr en termes de types et complique la coloration syntaxique, l'analyse de code, l'optimisation, etc.
Bien entendu, lorsque vous utilisez des outils ou des cadres externes au langage Java, de nouveaux facteurs peuvent entrer en jeu.
Il pourrait y avoir une différence perceptible.
String.format
est assez complexe et utilise une expression régulière en dessous. Ne prenez donc pas l'habitude de l'utiliser partout, mais uniquement là où vous en avez besoin.
StringBuilder
serait un ordre de grandeur plus rapide (comme quelqu'un l'a déjà souligné).
Je n'ai pas fait de repère spécifique, mais je pense que la concaténation est peut-être plus rapide. String.format () crée un nouveau formateur qui, à son tour, crée un nouveau StringBuilder (avec une taille de 16 caractères seulement). Cela représente une surcharge considérable, en particulier si vous formatez une chaîne plus longue et que StringBuilder doit continuellement être redimensionné.
Cependant, la concaténation est moins utile et plus difficile à lire. Comme toujours, cela vaut la peine de faire un point de repère sur votre code pour voir lequel est le meilleur. Les différences peuvent être négligeables dans l'application serveur une fois que vos ensembles de ressources, vos paramètres régionaux, etc. ont été chargés en mémoire et que le code est JITted.
Il serait peut-être judicieux de créer votre propre formateur avec un StringBuilder (Appendable) et un paramètre Locale correctement dimensionnés et de les utiliser si vous avez beaucoup de formatage à faire.
Je pense que nous pouvons utiliser MessageFormat.format
car il devrait être bon à la fois en termes de lisibilité et de performances.
J'ai utilisé le même programme que celui utilisé par Icaro dans sa réponse ci-dessus et je l'ai enrichi d'un code supplémentaire permettant d'utiliser MessageFormat
pour expliquer la performance. Nombres.
public static void main(String[] args) {
long start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = "Hi " + i + "; Hi to you " + i * 2;
}
long end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = String.format("Hi %s; Hi to you %s", i, +i * 2);
}
end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for (int i = 0; i < 1000000; i++) {
String s = MessageFormat.format("Hi %s; Hi to you %s", i, +i * 2);
}
end = System.currentTimeMillis();
System.out.println("MessageFormat = " + ((end - start)) + " millisecond");
}
Concaténation = 69 milliseconde
Format = 1435 milliseconde
MessageFormat = 200 milliseconde
MISES À JOUR:
Selon le rapport SonarLint, les chaînes au format Printf doivent être utilisées correctement (calmar: S3457)
Etant donné que les chaînes de format de style printf sont interprétées à l'exécution plutôt que validées par le compilateur, elles peuvent contenir des erreurs entraînant la création des chaînes incorrectes. Cette règle valide statiquement la corrélation des chaînes de format de style printf avec leurs arguments lors de l'appel des méthodes de formatage (...) de Java.util.Formatter, Java.lang.String, Java.io.PrintStream, MessageFormat et Java.io. Les classes .PrintWriter et les méthodes printf (...) des classes Java.io.PrintStream ou Java.io.PrintWriter.
Je remplace le style printf par les accolades et j'ai obtenu quelque chose d'intéressant comme ci-dessous.
Concaténation = 69 milliseconde
Format = 1107 milliseconde
Format: accolades = 416 millisecondes
MessageFormat = 215 milliseconde
MessageFormat: accolades = 2517 millisecondes
Ma conclusion:
Comme je l’ai souligné plus haut, l’utilisation de String.format avec des accolades devrait être un bon choix pour tirer parti d’une bonne lisibilité et de performances.
Vous ne pouvez pas comparer String Concatenation et String.Format par le programme ci-dessus.
Vous pouvez également essayer d’interchanger la position d’utilisation de votre String.Format et de votre concaténation dans votre bloc de code, comme ci-dessous.
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
for( int i=0;i<1000000; i++){
String s = String.format( "Hi %s; Hi to you %s",i, + i*2);
}
long end = System.currentTimeMillis();
System.out.println("Format = " + ((end - start)) + " millisecond");
start = System.currentTimeMillis();
for( int i=0;i<1000000; i++){
String s = "Hi " + i + "; Hi to you " + i*2;
}
end = System.currentTimeMillis();
System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;
}
Vous serez surpris de voir que Format fonctionne plus rapidement ici. En effet, les objets initiaux créés peuvent ne pas être libérés et il peut y avoir un problème d'allocation de mémoire et donc de performances.
Il faut un peu de temps pour s’habituer à String.Format, mais cela en vaut la peine dans la plupart des cas. Dans le monde NRA (il ne faut jamais rien répéter), il est extrêmement utile de conserver vos messages symbolisés (journalisation ou utilisateur) dans une bibliothèque Constant (je préfère ce qui est une classe statique) et de les appeler si nécessaire avec String.Format, que vous le fassiez ou non. sont localiser ou non. Essayer d'utiliser une telle bibliothèque avec une méthode de concaténation est plus difficile à lire, à résoudre, à relire et à gérer avec toute approche nécessitant une concaténation. Le remplacement est une option, mais je doute qu'il soit performant. Après des années d'utilisation, mon plus gros problème avec String.Format est que la durée de l'appel est trop longue lorsque je le passe dans une autre fonction (comme Msg), mais il est facile de se déplacer avec une fonction personnalisée pouvant servir d'alias. .