Borrar un búfer de cadena / generador después del bucle

122

¿Cómo se borra el búfer de cadena en Java después de un bucle para que la siguiente iteración utilice un búfer de cadena transparente?

cascada de lluvia
fuente
2
Otra pregunta: ¿por qué usa un SB solo para una iteración del ciclo? ¿Hay otra iteración interna? SB no vale la pena si simplemente quieres hacer A + B + C + D (el compilador de Java usará internamente un SB). Es útil si desea agregar condicionalmente partes de cadenas, pero de lo contrario ... simplemente use "+".
helios

Respuestas:

135

Una opción es utilizar el método de eliminación de la siguiente manera:

StringBuffer sb = new StringBuffer();
for (int n = 0; n < 10; n++) {
   sb.append("a");

   // This will clear the buffer
   sb.delete(0, sb.length());
}

Otra opción (limpiador de bits) usa setLength (int len) :

sb.setLength(0);

Consulte Javadoc para obtener más información:

Jon
fuente
15
Algo un poco menos basura sería simplemente declarar el StringBuffer dentro del bucle.
Mark Elliot
11
Ah, creo que sb.setLength (0); es más limpio y eficiente que declararlo dentro del ciclo. Su solución va en contra del beneficio de rendimiento de usar StringBuffer ...
Jon
6
Creo que el beneficio de rendimiento proviene de la mutabilidad de la cadena, no de guardar la instanciación. aquí hay una prueba rápida de iteraciones 1e8: bucle interno (2.97s): ideone.com/uyyTL14w , bucle externo (2.87s): ideone.com/F9lgsIxh
Mark Elliot
6
Hablando de rendimiento: a menos que se acceda a su código en un escenario de subprocesos múltiples, debe usar StringBuilder en lugar de StringBuffer; consulte javadoc: "Siempre que sea posible, se recomienda que esta clase se use en lugar de StringBuffer, ya que será más rápido en la mayoría de las implementaciones ".
Rahel Lüthy
4
El único beneficio de crear el SB en el exterior es no perder su carácter interno (potencialmente largo). Si en el primer iterador creció lo suficiente, el segundo ciclo no necesitará cambiar el tamaño de ningún carácter []. Pero para obtener la ventaja, el "método claro" tendrá que preservar el tamaño de la matriz interna. setLength hace eso, pero también establece en \ u0000 todos los caracteres no utilizados en el SB, por lo que tiene menos rendimiento que simplemente crear un nuevo SB con una buena capacidad inicial. Declarar dentro del bucle es mejor.
helios
48

La forma más sencilla de reutilizar StringBufferes utilizar el métodosetLength()

public void setLength(int newLength)

Puede tener el caso como

StringBuffer sb = new StringBuffer("HelloWorld");
// after many iterations and manipulations
sb.setLength(0);
// reuse sb
Mossaddeque Mahmood
fuente
2
@MMahmoud, en el ejemplo de código debería leer en setLengthlugar de setlength.
OnaBai
Cuando veo la fuente del código setLength, ¿cómo está funcionando ( gist.github.com/ebuildy/e91ac6af2ff8a6b1821d18abf2f8c9e1 ) aquí el recuento será siempre mayor que newLength (0)?
Thomas Decaux
20

Tienes dos opciones:

O use:

sb.setLength(0);  // It will just discard the previous data, which will be garbage collected later.  

O usar:

sb.delete(0, sb.length());  // A bit slower as it is used to delete sub sequence.  

NOTA

Evite declarar objetos StringBufferu StringBuilderobjetos dentro del ciclo, de lo contrario, creará nuevos objetos con cada iteración. La creación de objetos requiere recursos del sistema, espacio y también lleva tiempo. Por lo tanto, a largo plazo, evite declararlos dentro de un bucle si es posible.

Aditya Singh
fuente
5
buf.delete(0,  buf.length());
Suraj Chandran
fuente
5

Sugiero crear uno nuevo StringBuffer(o incluso mejor StringBuilder) para cada iteración. La diferencia de rendimiento es realmente insignificante, pero su código será más corto y sencillo.

Eli Acherkan
fuente
2
Si tiene alguna evidencia de dicha diferencia de rendimiento, compártala. (También me alegrará ver estadísticas de dónde se usa principalmente Java y bajo qué definición de "principalmente".)
Eli Acherkan
1
Es bien sabido que la asignación (nueva palabra clave en Java) es más cara que simplemente cambiar algunos campos del mismo objeto. Para "la mayoría", que podría reemplazar con altamente, hay más de 1.500 millones de dispositivos Android, todos usando Java.
Louis CAD
1
Estoy de acuerdo en que en algunos casos, la legibilidad del código es más importante que el rendimiento, pero en este caso, ¡es solo una línea de código! Y puede ejecutar una evaluación comparativa que le demostrará que la asignación y la creación de objetos, además de la recolección de basura, es más costosa que no hacerlo en absoluto. Como estamos en un bucle, es más que relevante.
Louis CAD
1
También suscribí la opinión de Knuth, pero su punto de vista no siempre es cierto. Agregar solo una línea para potencialmente ganar ciclos de CPU y memoria no es nada malo. Estás tomando la idea demasiado en serio. Tenga en cuenta que un bucle generalmente se repite muchas veces. Un bucle se puede repetir miles de veces, lo que puede consumir preciosos megabytes en un dispositivo móvil (y también en un servidor o computadora) si no se optimiza con cuidado.
Louis CAD
1
Creo que ambos expresamos nuestros puntos de vista con suficiente claridad y creo que una discusión más profunda no será beneficiosa para esta sección de comentarios. Si cree que mi respuesta es incorrecta, engañosa o incompleta, entonces usted, o cualquier persona, es bienvenido a editarla y / o rechazarla.
Eli Acherkan
5
public void clear(StringBuilder s) {
    s.setLength(0);
}

Uso:

StringBuilder v = new StringBuilder();
clear(v);

para mayor legibilidad, creo que esta es la mejor solución.

Ido Kessler
fuente
2

Ya hay una buena respuesta. Simplemente agregue un resultado de referencia para la diferencia de rendimiento de StringBuffer y StringBuild use una nueva instancia en el bucle o use setLength (0) en el bucle.

El resumen es: En un gran bucle

  • StringBuilder es mucho más rápido que StringBuffer
  • Crear una nueva instancia de StringBuilder en bucle no tiene ninguna diferencia con setLength (0). (setLength (0) tiene una ventaja muy, muy pequeña que la de crear una nueva instancia).
  • StringBuffer es más lento que StringBuilder al crear una nueva instancia en bucle
  • setLength (0) de StringBuffer es extremadamente más lento que crear una nueva instancia en bucle.

Punto de referencia muy simple (acabo de cambiar manualmente el código y hacer una prueba diferente):

public class StringBuilderSpeed {
public static final char ch[] = new char[]{'a','b','c','d','e','f','g','h','i'};

public static void main(String a[]){
    int loopTime = 99999999;
    long startTime = System.currentTimeMillis();
    StringBuilder sb = new StringBuilder();
    for(int i = 0 ; i < loopTime; i++){
        for(char c : ch){
            sb.append(c);
        }
        sb.setLength(0);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("Time cost: " + (endTime - startTime));
}

}

Nueva instancia de StringBuilder en bucle: costo de tiempo: 3693, 3862, 3624, 3742

Conjunto de StringBuilder Longitud: Coste de tiempo: 3465, 3421, 3557, 3408

Nueva instancia de StringBuffer en bucle: costo de tiempo: 8327, 8324, 8284

StringBuffer setLength Costo de tiempo: 22878, 23017, 22894

Nuevamente StringBuilder setLength para asegurar que mi laboratorio no tenga algún problema para usar tan largo para StringBuffer setLength :-) Costo de tiempo: 3448

Hao
fuente
0
StringBuffer sb = new SringBuffer();
// do something wiht it
sb = new StringBuffer();

Creo que este código es más rápido.

Binh Phung
fuente
3
Esto tendrá problemas de rendimiento. Crea un nuevo objeto después de cada iteración
Aditya Singh
@AdityaSingh Es un enfoque perfectamente razonable. No asuma problemas de desempeño sin evidencia.
shmosel
Asumir cosas basadas en la comprensión de lo que está sucediendo es la base de la inteligencia.
rghome