2011-08-23 9 views
11

A menudo se argumenta que evitar la creación de objetos (especialmente en bucles) se considera una buena práctica.¿Qué es más eficiente StringBuffer new() o delete (0, sb.length())?

Entonces, ¿qué es lo más eficiente con respecto a StringBuffer?

StringBuffer sb = new StringBuffer(); 
ObjectInputStream ois = ...; 

for (int i=0;i<1000;i++) { 

    for (j=0;i<10;j++) { 
     sb.append(ois.readUTF()); 
    } 
    ... 

    // Which option is the most efficient? 
    sb = new StringBuffer(); // new StringBuffer instance? 
    sb.delete(0,sb.length()); // or deleting content? 

} 

Es decir, se podría argumentar que la creación de un objeto es más rápida que el bucle en una matriz.

+1

¿Lo ha probado en un analizador? ¿Cuáles fueron los resultados? –

+0

Vea también http://codereview.stackexchange.com/questions/7575/reusing-stringbuilder-or-creating-a-new-one – Flow

Respuesta

15

Primero StringBuffer es seguro para subprocesos que tendrá un mal rendimiento en comparación con StringBuilder. StringBuilder no es seguro para subprocesos, pero como resultado es más rápido. Finalmente, prefiero establecer la longitud en 0 usando setLength.

sb.setLength(0) 

Esto es similar a .delete(...), excepto que realmente no se preocupan por la longitud. También es probable que sea un poco más rápido ya que no es necesario 'eliminar' nada. Crear un nuevo StringBuilder (o StringBuffer) sería menos eficiente. Cada vez que vea new Java está creando un nuevo objeto y colocándolo en el montón.

Nota: Después de buscar en la implementación de .delete y .setLength, longitud .delete conjuntos = 0, y .setLength conjuntos cada cosa a '\0' por lo que puede ser un poco victoria con .delete

+0

Si 'StringBuilder' obtiene un búfer interno muy grande asignado que se desperdicia porque un ciclo temprano la iteración produce una cadena muy grande, y las iteraciones posteriores no, entonces puede terminar golpeando más tiempo de lo necesario, pero mantener el buffer es una ganancia neta ya que si las iteraciones posteriores producen cadenas casi del mismo tamaño que las iteraciones anteriores, está evitando algunas copias de buffer –

+1

'delete' no es O (N) cuando la eliminación borra todo el contenido. –

+0

Esto es cierto, estoy actualizando mi respuesta. Pero solo cuando len == count que es verdadero si está borrando todo. –

1

El método de eliminación se lleva a cabo esta manera:

public AbstractStringBuilder delete(int start, int end) { 
    if (start < 0) 
     throw new StringIndexOutOfBoundsException(start); 
    if (end > count) 
     end = count; 
    if (start > end) 
     throw new StringIndexOutOfBoundsException(); 
    int len = end - start; 
    if (len > 0) { 
     System.arraycopy(value, start+len, value, start, count-end); 
     count -= len; 
    } 
    return this; 
} 

Como puede ver, no itera a través de la matriz.

+2

¿Qué piensas 'System.arraycopy'? –

+0

@Mike No estoy seguro de lo que quiere decir 'len' será 0. Se calcula a partir de' end - start' que 'sb.length() - 0'. Lo que quieres decir con 'count-end' será 0, que es verdadero. Pero generalmente no se calcula O (n) para solo un conjunto de params. –

+0

@Amir, lo siento, creo que tenía razón en cuanto a que no se estaba haciendo un trabajo importante, pero me equivocaba sobre por qué. Cuando 'start' es 0 y' end' es 'count' alias' this.length() ', entonces terminas haciendo' System.arraycopy (value, count, value, 0, 0) '. El último 0 significa que está copiando 0 bytes del recuento de índice al índice 0. –

3

para poder amplificar los comentarios anteriores:

De su fuente, delete() llama siempre System.arraycopy(), pero si los argumentos son (0, count), se llamará arraycopy() con una longitud de cero, que presumiblemente no tendrá ningún efecto. En mi humilde opinión, esto debería optimizarse ya que apuesto a que es el caso más común, pero no importa.

Con setLength(), por el contrario, la llamada se incrementará la capacidad de la StringBuilder si es necesario a través de una llamada a ensureCapacityInternal() (otro caso muy común que debería haber sido optimizado a cabo en mi humilde opinión) y luego se trunca la longitud que delete() habría hecho.

En última instancia, ambos métodos simplemente terminan configurando count a cero.

Ninguna llamada realiza ninguna iteración en este caso. Ambos hacen una llamada de función innecesaria. Sin embargo, ensureCapacityInternal() es un método privado muy simple, que invita al compilador a optimizarlo casi fuera de existencia por lo que es probable que setLength() sea un poco más eficiente.

soy muy escéptico de que la creación de una nueva instancia de StringBuilder jamás podría ser tan eficiente como un simple ajuste de count a cero, pero supongo que el compilador podría reconocer el patrón involucrados y convertir las repetidas instancias en las repetidas llamadas a setLength (0). Pero en el mejor de los casos, sería un lavado. Y depende del compilador para reconocer el caso.

Resumen ejecutivo: setLength (0) es el más eficiente. Para una eficiencia máxima, preasigne el espacio de búfer en StringBuilder cuando lo cree.

Cuestiones relacionadas