2011-02-11 18 views
26

Al hacer la concatenación de un montón de cuerdas, que se han recomendado para hacerlo usando un StringBuilder como tal:StringBuilder vs Cadena considerando reemplazar

StringBuilder someString = new StringBuilder("abc"); 
someString.append("def"); 
someString.append("123"); 
someString.append("moreStuff"); 

en contraposición a

String someString = "abc"; 
someString = someString + "def"; 
someString = someString + "123"; 
someString = someString + "moreStuff"; 

que daría lugar a la creación de bastantes Cuerdas, a diferencia de una.

Ahora, tengo que hacer una cosa similar, pero en lugar de utilizar la concatenación que utilizar el método de cadena replace como tal:

String someString = SOME_LARGE_STRING_CONSTANT; 
someString = someString.replace("$VARIABLE1", "abc"); 
someString = someString.replace("$VARIABLE2", "def"); 
someString = someString.replace("$VARIABLE3", "123"); 
someString = someString.replace("$VARIABLE4", "moreStuff"); 

Para obtener el mismo resultado utilizando StringBuilder, tengo que hacer esto, sólo por una reemplazar:

someString.replace(someString.indexOf("$VARIABLE1"), someString.indexOf("$VARIABLE1")+10, "abc"); 

Así que mi pregunta es: "¿es mejor utilizar String.replace y tienen un montón de cadenas adicionales creados, o para usar StringBuilder todavía, y tienen un montón de líneas de largo aliento como el ¿uno arriba?

+0

Si está generando problemas de Desempeño, cambiarlo. Si no hay cambios más importantes que realizar, cámbialo.Si la entrada es realmente muy grande y se usa con mucha frecuencia, cámbiela. Por cierto, el segundo enfoque, no funcionará, ya que solo lo reemplazará una vez, tiene que ponerlo en un ciclo while. Eche un vistazo a mi respuesta. – OscarRyz

Respuesta

38

Es cierto que StringBuilder tiende a ser mejor que concatenar o modificar Cadenas manualmente, ya que StringBuilder es mutable, mientras que String es inmutable y necesita crear una nueva Cadena para cada modificación.

para notar, sin embargo, el compilador de Java se convertirá automáticamente en un ejemplo como este:

String result = someString + someOtherString + anotherString; 

en algo así como:

String result = new StringBuilder().append(someString).append(someOtherString).append(anotherString).toString(); 

Dicho esto, a menos que usted está reemplazando una porción entera de Strings, elija el que sea más legible y más fácil de mantener. Entonces, si puede mantenerlo más limpio teniendo una secuencia de llamadas de 'reemplazo', continúe y haga eso sobre el método StringBuilder. La diferencia será insignificante en comparación con el estrés que ahorra al tratar con the sad tragedy of micro-optimizations.

PS

Para su ejemplo de código (que, como OscarRyz señaló, no va a funcionar si usted tiene más de un "$VARIABLE1" en someString, en cuyo caso se tendrá que utilizar un bucle), usted podría almacenar en caché el resultado de la llamada en indexOf:

someString.replace(someString.indexOf("$VARIABLE1"), someString.indexOf("$VARIABLE1")+10, "abc"); 

con

int index = someString.indexOf("$VARIABLE1");  
someString.replace(index, index+10, "abc"); 

No hay necesidad de buscar en la cadena dos veces :-)

+0

Esto falla si tiene dos $ VARIABLE1 en la entrada (solo reemplaza al primero. Tiene que ponerlo en un ciclo while. – OscarRyz

+0

@OscarRyz D'oh! Es cierto. Ni siquiera noté que :-p. razón por la que escribí que el código de muestra fue solo para indicarle al interrogador que no deberían tener que llamar al índice dos veces. –

+0

Lo cual es un buen punto por cierto. Intenté lo mismo en una muestra y me di cuenta hasta entonces. Es fácil olvidarlo . Ver mi ans – OscarRyz

0

puede ser la clase String utiliza internamente

método indexOf

para encontrar el índice de la cadena antigua y reemplazarla con una nueva cadena.

Y también StringBuilder no es seguro en cuanto a los hilos, por lo que se ejecuta mucho más rápido.

1

En lugar de tener líneas largas como esa, usted podría escribir un método para la sustitución de partes de cadenas de StringBuilder, algo parecido a esto:

public StringBuilder replace(StringBuilder someString, String replaceWhat, String replaceWith) { 
    return someString.replace(someString.indexOf(replaceWhat), someString.indexOf(replaceWhat)+replaceWhat.length(), replaceWith); 
} 
+0

Estaba pensando en el s ame, pero esto necesita un ciclo while para reemplazarlos a todos, y no solo la primera vez. – OscarRyz

0

Si la cadena es realmente grande y que está preocupado sobre el rendimiento. Yo recomendaría escribir una clase que tome su plantilla de texto y una lista de variables, luego lee la cadena fuente carácter por carácter y construye el resultado usando StringBuilder. Esa debería ser la más eficiente tanto en términos de uso de CPU y memoria. Además, si está leyendo este texto de plantilla de un archivo, no lo cargaría todo en la memoria. Procese en fragmentos mientras lo lee desde el archivo.

Si solo busca una buena forma de construir una cadena que no sea tan eficiente como StringBuilder pero más eficiente que agregar cadenas una y otra vez, puede usar String.format(). Funciona como sprintf() en C. MessageFormat.format() también es una opción pero usa StringBuffer.

Hay otra pregunta relacionada aquí: Inserting a Java string in another string without concatenation?

7

Adivina qué? Si está ejecutando con Java 1.5 + la concatenación funciona de la misma con los literales de cadena

String h = "hello" + "world"; 

y

String i = new StringBuilder().append("hello").append("world").toString(); 

son los mismos.

Por lo tanto, el compilador ya hizo el trabajo por usted.

por supuesto, mejor sería:

String j = "hellworld"; // ;) 

En cuanto a la segunda, yeap, eso es preferible, pero DEBERÍAMOS ser tan difícil, con el poder de "buscar y reemplazar" y un poco de foo expresiones regulares

Por ejemplo, puede definir un método como el que en esta muestra:

public static void replace(String target, String replacement, 
           StringBuilder builder) { 
    int indexOfTarget = -1; 
    while((indexOfTarget = builder.indexOf(target)) >= 0) { 
     builder.replace(indexOfTarget, indexOfTarget + target.length() , replacement); 
    } 
    } 

Y su código actualmente se ve así:

someString = someString.replace("VARIABLE1", "abc"); 
someString = someString.replace("VARIABLE2", "xyz"); 

Todo lo que tiene que hacer es editor de texto agarrar un disparador algo como esto VI de búsqueda y reemplazo:

%s/^.*("\(.*\)".\s"\(.*\)");/replace("\1","\2",builder); 

que decía: "tomar nada entre paréntesis y que se parece a una cadena literal, y puso en esta otra cadena ".

y su código se verá a partir de esto:

someString = someString.replace("VARIABLE1", "abc"); 
someString = someString.replace("VARIABLE2", "xyz"); 

a esto:

replace("VARIABLE1", "abc", builder); 
replace("VARIABLE2", "xyz", builder); 

en ningún momento.

He aquí una demostración de trabajo:

class DoReplace { 
    public static void main(String ... args) { 
    StringBuilder builder = new StringBuilder(
     "LONG CONSTANT WITH VARIABLE1 and VARIABLE2 and VARIABLE1 and VARIABLE2"); 
    replace("VARIABLE1", "abc", builder); 
    replace("VARIABLE2", "xyz", builder); 
    System.out.println(builder.toString()); 
    } 
    public static void replace(String target, String replacement, 
           StringBuilder builder) { 
    int indexOfTarget = -1; 
    while((indexOfTarget = builder.indexOf(target)) > 0) { 
     builder.replace(indexOfTarget, indexOfTarget + target.length() , 
         replacement); 
    } 
    } 
} 
+0

p.s ¿Qué sucede si la instancia del objetivo comienza en la posición 0 ??? Tu condición de salir de lazo while indica que no la reemplazaría. –

+0

@Matthew meh ... tiene razón – OscarRyz

+2

En realidad, en su primer ejemplo, el compilador, de hecho, no genera la solución StringBuilder, sino su tercera línea de código, ya que es una constante de tiempo de compilación. Entonces estos son equivalentes. –

3

, diría que ir para el uso de StringBuilder sino simplemente escribir un envoltorio que facilita hacer el código más legible y por lo tanto más fácil de mantener, al tiempo que se mantiene la eficiencia. = D

import java.lang.StringBuilder; 
public class MyStringBuilder 
{ 
    StringBuilder sb; 

    public MyStringBuilder() 
    { 
     sb = new StringBuilder(); 
    } 

    public void replace(String oldStr, String newStr) 
    { 
      int start = -1; 
      while ((start = sb.indexOf(oldStr)) > -1) 
      { 
        int end = start + oldStr.length(); 
        sb.replace(start, end, newStr); 
      } 
    } 

    public void append(String str) 
    { 
     sb.append(str); 
    } 

    public String toString() 
    { 
      return sb.toString(); 
    } 

    //.... other exposed methods 

    public static void main(String[] args) 
    { 
      MyStringBuilder sb = new MyStringBuilder(); 
      sb.append("old old olD dudely dowrite == pwn"); 
      sb.replace("old", "new"); 
      System.out.println(sb); 
    } 
} 

SALIDA:

new new olD dudely dowrite == pwn 

Ahora usted puede simplemente utilizar la nueva versión que es un trazador de líneas fácil

MyStringBuilder mySB = new MyStringBuilder(); 
mySB.append("old dudley dowrite == pwn"); 
mySB.replace("old", "new"): 
+0

Falla con la entrada '" old old dudley "' – OscarRyz

+0

@OscarRyz Interesante. Lo tengo en un proyecto y el resultado es exactamente lo que se espera. Teniendo en cuenta que no agregué los métodos append (String) o String toString(). Decidí agregarlos para mayor claridad –

+0

Probablemente nunca tenga más de 1 cadena para reemplazar. Intenta con '" old old dudley "' y obtendrás '" new old dudley "' – OscarRyz

0

códigos de todos los chicos tienen un error .try yourReplace("x","xy") . Lo hará bucle infinitamente

+0

Hice este contenedor sin ese problema https://gist.github.com/ipoletti/c58902bb9571c8cdc527 –

0

Jam Hong está en lo cierto: todas las soluciones anteriores tienen el potencial de repetirse infinitamente. Supongo que la lección que debemos sacar es que las micro optimizaciones a menudo pueden causar todo tipo de problemas horribles y realmente no te ahorran mucho. Aún así, sea como sea, aquí hay una solución que no tendrá un ciclo infinito.

private static void replaceAll(StringBuilder builder, String replaceWhat, String replaceWith){ 
    int occuranceIndex = builder.indexOf(replaceWhat); 
    int lastReplace = -1; 
    while(occuranceIndex >= 0){ 
     if(occuranceIndex >= lastReplace){ 
      builder.replace(occuranceIndex, occuranceIndex+replaceWhat.length(), replaceWith); 
      lastReplace = occuranceIndex + replaceWith.length(); 
      occuranceIndex = builder.indexOf(replaceWhat); 
     }else{ 
      break; 
     } 
    } 
} 
+0

Esto actúa como replaceFirst para StringBuilder builder = new StringBuilder ("Var x y x y x y x"); replaceAll (constructor, "x", "xy"); – irmu

0

si bien es cierto que las micro optimizaciones pueden ser problemáticos, a veces depende del contexto, por ejemplo, si su sustitución pasa a ejecutar dentro de un bucle con 10000 iteraciones, el verá una diferencia significativa del rendimiento de la optimizaciones "inútiles".

en la mayoría de casos, sin embargo, es mejor errar por el lado de la legibilidad