web-dev-qa-db-fra.com

Est-il préférable d’utiliser String.format sur la concaténation de chaînes en Java?

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.

244
Omar Kooheji

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)
222
workmad3

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:

  • Concaténation = 265 milliseconde
  • Format = 4141 milliseconde

Par conséquent, la concaténation est beaucoup plus rapide que String.format.

149
Icaro

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");
  • 2012-01-11 16: 30: 46,058 INFO [TestMain] - Format = 1416 milliseconde
  • 2012-01-11 16: 30: 46,190 INFO [TestMain] - Concaténation = 134 millisecondes
  • 2012-01-11 16: 30: 46,313 INFO [TestMain] - Constructeur de chaînes = 117 millisecondes
36
TechTrip

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 IllegalFormatExceptionau 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.

34
Martin Schröder

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.

17
Thilo

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
14
Derek Ziemba

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
9
Akos Cz

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.

6
Fortega

Généralement, la concaténation de chaînes est préférable à String.format. Ce dernier a deux inconvénients principaux:

  1. Il ne code pas la chaîne à construire de manière locale.
  2. Le processus de construction est codé dans une chaîne.

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.

4
Stephane Bersier

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é).

2
Pawel Zieminski

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.

2
AngerClown

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.

0

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.

0
DotNetUser

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. .

0
user665056