¿Es mejor práctica utilizar String.Format sobre la concatenación de cadenas en Java?
-
06-09-2019 - |
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.
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:
- no codifica la cadena que se construirá de manera local.
- 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.