salto - ¿Es una mejor práctica utilizar String.format sobre la concatenación de cadenas en Java?
manejo de cadenas en java (12)
¿Hay una diferencia perceptible entre el uso de String.Format
y la concatenación de cadenas en Java?
String.format
a usar String.format
pero de vez en cuando se desliza y usa un concat. Me preguntaba si uno era mejor que el otro.
String.Format
como lo veo, String.Format
le da más poder para "formatear" la cadena; y la concatenación significa que no tiene que preocuparse por poner accidentalmente un% s extra o perder uno.
String.format
también es más corto.
Cuál es más legible depende de cómo funciona tu cabeza.
Cuál es más legible depende de cómo funciona tu cabeza.
Tienes tu respuesta allí mismo.
Es una cuestión de gusto personal.
La concatenación de cuerdas es ligeramente más rápida, supongo, pero eso debería ser insignificante.
Aquí está la misma prueba que la anterior con la modificación de llamar al método toString () en el StringBuilder . Los resultados a continuación muestran que el enfoque de StringBuilder es 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 de 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
Aquí hay 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
En general, la concatenación de cadenas se debe String.format
a String.format
. Este último tiene dos desventajas principales:
- No codifica la cadena que se construirá de manera local.
- El proceso de construcción está codificado en una cadena.
Por punto 1, quiero decir que no es posible entender lo que está haciendo una llamada String.format()
en un solo paso secuencial. Se obliga a uno a ir y venir entre la cadena de formato y los argumentos, mientras se cuenta la posición de los argumentos. Para concatenaciones cortas, esto no es un gran problema. En estos casos, sin embargo, la concatenación de cadenas es menos verbosa.
Por el punto 2, quiero decir que la parte importante del proceso de construcción está codificada en la cadena de formato (usando un DSL). Usar cadenas para representar el código tiene muchas desventajas. No es intrínsecamente seguro para los tipos y complica el resaltado de sintaxis, el análisis de código, la optimización, etc.
Por supuesto, cuando se usan herramientas o marcos externos al lenguaje Java, pueden entrar en juego nuevos factores.
No he hecho ningún punto de referencia específico, pero creo que la concatenación puede ser más rápida. String.format () crea un nuevo formateador que, a su vez, crea un nuevo StringBuilder (con un tamaño de solo 16 caracteres). Eso es una gran cantidad de gastos generales, especialmente si está formateando 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 una 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 que los paquetes de recursos, las configuraciones regionales, etc. se carguen en la memoria y el código esté JITted.
Tal vez, como práctica recomendada, sería una buena idea crear su propio Formateador con un StringBuilder (Anexable) y una configuración regional del tamaño adecuado y usarlo si tiene mucho formato para hacer.
No puede comparar Strat Concatenation y String.Format por el programa anterior.
Puede intentar esto también intercambiando la posición de usar su String.Format y Concatenation en su 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") ;
}
Te sorprenderá ver que el Formato funciona más rápido aquí. Esto se debe a que los objetos iniciales creados pueden no ser liberados y puede haber un problema con la asignación de memoria y, por lo tanto, con el rendimiento.
Podría haber una diferencia perceptible.
String.format
es bastante complejo y utiliza una expresión regular debajo, así que no hagas el hábito de usarlo en todas partes, sino solo donde lo necesites.
StringBuilder
sería un orden de magnitud más rápido (como alguien señalado aquí).
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 del tiempo son los siguientes:
- Concatenación = 265 milisegundos
- Formato = 4141 milisegundos
Por lo tanto, la concatenación es mucho más rápida que String.format.
Sugeriría que es una mejor práctica usar String.format()
. La razón principal es que String.format()
se puede localizar más fácilmente con texto cargado desde archivos de recursos, mientras que la concatenación no se puede localizar sin producir un nuevo ejecutable con un código diferente para cada idioma.
Si planea que su aplicación sea localizable, también debe adquirir el hábito de especificar posiciones de argumentos para sus tokens de formato también:
"Hello %1$s the time is %2$t"
Esto puede ser localizado y se pueden intercambiar los tokens de nombre y tiempo sin requerir una recompilación del ejecutable para tener en cuenta el orden diferente. Con las posiciones de argumento, también puede reutilizar 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)
Un problema con .format
es que se pierde la seguridad de tipo estático. Puede tener muy pocos argumentos para su formato, y puede tener los tipos incorrectos para los especificadores de formato, lo que lleva a una IllegalFormatException
en tiempo de ejecución , por lo que podría terminar con un código de registro que interrumpa la producción.
En contraste, los argumentos a +
pueden ser probados por el compilador.
Ya que hay una discusión sobre el rendimiento, pensé que agregaría en una comparación que incluía StringBuilder. De hecho, es más rápido que el Concat y, naturalmente, la opción String.format.
Para hacer de este un tipo de comparación manzanas con manzanas, instalo un nuevo StringBuilder en el bucle en lugar de hacerlo en el exterior (esto es en realidad más rápido que hacer solo una instancia, probablemente debido a la sobrecarga de reasignación de espacio para el bucle adjunto al 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
String.format()
es algo más que concatenar cadenas. Por ejemplo, puede mostrar números en una configuración regional específica utilizando String.format()
.
Sin embargo, si no te importa la localización, no hay diferencia funcional. Tal vez el uno sea más rápido que el otro, pero en la mayoría de los casos será despreciable.