Pregunta

¿Hay una diferencia perceptible entre el uso de String.format y concatenación de cadenas en Java?

Yo tiendo a usar String.format pero en ocasiones se deslice y utilizar una concatenación. Me preguntaba si uno era mejor que el otro.

La forma en que lo veo, String.format le da más poder en "formatear" la cadena; y la concatenación significa que usted no tiene que preocuparse por ponga accidentalmente en un% s más o de menos uno.

String.format es también más corta.

¿Cuál es más fácil de leer depende de cómo funciona su cabeza.

¿Fue útil?

Solución

Me gustaría sugerir que es mejor práctica utilizar String.format(). La razón principal es que String.format() puede ser más fácilmente localizada con el texto cargado desde archivos de recursos, mientras que la concatenación no puede localizar sin producir un nuevo ejecutable con código diferente para cada idioma.

Si usted planea en su aplicación para estar localizable también debe adquirir el hábito de especificar las posiciones de argumentos para el formato de tokens así:

"Hello %1$s the time is %2$t"

Esto puede ser localizada y tienen el nombre y la hora tokens intercambiado sin necesidad de recompilar el ejecutable para dar cuenta de los diferentes pedidos. Con posiciones de argumentos también se puede volver a utilizar el mismo argumento sin pasarlo a la función dos veces:

String.format("Hello %1$s, your name is %1$s and the time is %2$t", name, time)

Otros consejos

Sobre el rendimiento:

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");
}

Los resultados de temporización son los siguientes:

  • Concatenación = 265 milisegundos
  • Formato = 4141 milisegundos

Por lo tanto, la concatenación es mucho más rápido que String.Format.

Dado que no hay discusión sobre el rendimiento pensé que había que añadir en una comparación que incluyó StringBuilder. De hecho, es más rápido que el concat y, naturalmente, la opción String.Format.

Para que esto sea una especie de comparación de manzanas con las manzanas que instanciar un nuevo StringBuilder en el bucle en lugar de fuera (esto es en realidad más rápido que hacerlo sólo una ejemplificación más probable debido a la sobrecarga de reasignación de espacio para el bucle anexar al el final de un constructor).

    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] - Formato = 1416 milisegundos
  • 2012-01-11 16: 30: 46 190 INFO [TestMain] - Concatenación = 134 milisegundos
  • 2012-01-11 16: 30: 46 313 INFO [TestMain] - Generador de cadenas = 117 milisegundos

Uno de los problemas con .format es que se pierde la seguridad de tipo estático. Puede tener muy pocos argumentos para el formato, y usted puede tener el tipo equivocado de los especificadores de formato -. Tanto conducen a una IllegalFormatException en tiempo de ejecución , por lo que podría terminar con el código de registro que rompe la producción

En contraste, los argumentos a + pueden ser probados por el compilador.

  

¿Cuál es más fácil de leer depende de cómo funciona su cabeza.

Tienes tu respuesta correcta allí.

Es una cuestión de gusto personal.

La concatenación de cadenas es ligeramente más rápido, supongo, pero que debería ser insignificante.

Aquí está una prueba con múltiples tamaños de muestra en milisegundos.

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

Aquí está la misma prueba que el anterior con la modificación de llamar a la toString () Método en el StringBuilder . Los resultados a continuación muestran que el enfoque StringBuilder es sólo un poco más lento que la concatenación de cadenas utilizando el + operador.

archivo: 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");

  }
}

Comandos del shell: (compilar y ejecutar StringTest 5 veces)

> javac StringTest.java
> sh -c "for i in \$(seq 1 5); do echo \"Run \${i}\"; java StringTest; done"

Resultados:

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() es más que simplemente concatenando cadenas. Por ejemplo, puede mostrar números en un lugar específico utilizando String.format().

Sin embargo, si no se preocupan por la localización, no hay diferencia funcional. Tal vez el uno es más rápido que el otro, pero en la mayoría de los casos será insignificante ..

En general, la concatenación de cadenas debe ser preferidas sobre String.format. Este último tiene dos desventajas principales:

  1. no codifica la cadena que se construirá de manera local.
  2. El proceso de construcción se codifica en una cadena.

Por el punto 1, quiero decir que no es posible comprender lo que una llamada String.format() está haciendo en una sola pasada secuencial. Uno se ve obligado a ir y venir entre la cadena de formato y los argumentos, mientras cuenta la posición de los argumentos. Para concatenaciones cortos, esto no es un gran problema. En estos casos, sin embargo, la concatenación de cadenas es menos detallado.

Por el punto 2, quiero decir que la parte importante del proceso de construcción se codifica en el cadena de formato (usando una conexión DSL). El uso de cadenas para representar código tiene muchas desventajas. No es de tipo seguro inherentemente, y complica resaltado de sintaxis, análisis de código, optimización, etc.

Por supuesto, cuando se utilizan herramientas o marcos externos al lenguaje Java, los nuevos factores pueden entrar en juego.

No he hecho ningún puntos de referencia específicos, pero me gustaría pensar que la concatenación puede ser más rápido. String.format () crea un nuevo formateador que, a su vez, crea un nuevo StringBuilder (con un tamaño de sólo 16 caracteres). Eso es una buena cantidad de sobrecarga sobre todo si está dando formato a una cadena más larga y StringBuilder sigue teniendo que cambiar el tamaño.

Sin embargo, la concatenación es menos útil y más difícil de leer. Como siempre, vale la pena hacer un punto de referencia en su código para ver cuál es mejor. Las diferencias pueden ser insignificantes en la aplicación del servidor después de su paquetes de recursos, locales, etc se cargan en la memoria y el código es compilados JIT.

Tal vez como una buena práctica, sería una buena idea para crear su propio formateador con un tamaño adecuado StringBuilder (Anexables) y la configuración regional y el uso que si usted tiene una gran cantidad de formateo de hacer.

Podría haber una diferencia perceptible.

String.format es bastante compleja y utiliza una expresión regular por debajo, por lo que no lo convierten en un hábito para usarlo en todas partes, pero sólo cuando lo necesite.

StringBuilder sería un orden de magnitud más rápido (como alguien aquí ya se señaló).

No se puede comparar Concatenación de cadenas y String.Format por el programa anterior.

Puede intentar esto también puede intercambiar la posición de la utilización de su String.Format y concatenación en el bloque de código como el siguiente

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") ;
}

Usted se sorprenderá al ver que formato funciona más rápido aquí. Esto es ya que los objetos creados intial no podrían ser liberados y no puede haber un problema con la asignación de memoria y por lo tanto el rendimiento.

Se necesita un poco de tiempo para acostumbrarse a String.Format, pero vale la pena en la mayoría de los casos. En el mundo de la NRA (nunca repetir nada) es extremadamente útil para mantener los mensajes de registro tokenizados (o usuario) en una biblioteca Constante (yo prefiero lo que equivale a una clase estática) y llamarlos según sea necesario con String.Format independientemente de si está localizando o no. Tratando de utilizar una biblioteca de este tipo con un método de concatenación es más difícil de leer, solucionar problemas, corregir y manejar con cualquier cualquier enfoque que requiere la concatenación. El reemplazo es una opción, pero dudo que sea performant. Después de años de uso, mi mayor problema con String.Format es la duración de la llamada es inconvenientemente largo cuando estoy pasando en otra función (como MSG), pero eso es fácil de recorrer con una función personalizada para servir como un alias .

Creo que podemos llegar con MessageFormat.format como debe ser bueno en ambos aspectos de rendimiento y legibilidad también.

He utilizado el mismo programa que se utiliza por Icaro en su respuesta anterior y yo aumenté con anexar código para utilizar MessageFormat para explicar los números de rendimiento.

  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");
  }
  

Concatenación = 69 milisegundos

     

Formato = 1435 milisegundos

     

MessageFormat = 200 milisegundos

Actualizaciones:

Según SonarLint Informe, las cadenas de formato de estilo Printf deben ser utilizados correctamente (calamar: S3457)

Debido a que las cadenas de formato de estilo printf se interpretan en tiempo de ejecución, en lugar de validados por el compilador, que puede contener errores que dan lugar a las cuerdas equivocadas están creando. Esta regla valida estáticamente la correlación de las cadenas de formato de estilo printf a sus argumentos al llamar al formato (...) los métodos de java.util.Formatter, java.lang.String, java.io.PrintStream, MessageFormat y java.io .PrintWriter clases y el printf (...) los métodos de clases java.io.PrintStream o java.io.PrintWriter.

puedo reemplazar el estilo printf con los soportes en rizadas y me dieron resultados algo interesante como a continuación.

  

La concatenación = 69 milisegundos
Formato = 1107 milisegundos
   Formato: rizado-paréntesis = 416 milisegundos
MessageFormat = 215   milisegundo
MessageFormat: rizado-paréntesis = 2517 milisegundos

Mi conclusión:
Como he señalado anteriormente, utilizando String.Format con rizado soportes en que debería ser una buena opción para obtener beneficios de una buena legibilidad y también el rendimiento.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top