Primero debemos recordar las características de tres:
1. Definición
Al mirar la API, encontrará que String, StringBuffer y StringBuilder implementan la interfaz CharSequence. Aunque todos están relacionados con las cuerdas, sus mecanismos de procesamiento son diferentes.
2. Use escenarios
Escenarios utilizando la clase de cadena: en escenarios donde las cadenas no cambian con frecuencia, se pueden usar clases de cadena, como declaraciones de constantes y un pequeño número de operaciones variables.
Escenarios que usan la clase StringBuffer: cuando las operaciones de cadena se realizan con frecuencia (como empalme, sustitución, eliminación, etc.) y en ejecución en un entorno de múltiples subprocesos, puede considerar usar StringBuffer, como el análisis XML, el parsante de parámetros HTTP y la encapsulación.
Escenarios utilizando la clase StringBuilder: cuando las operaciones de cadena se realizan con frecuencia (como empalme, sustitución y eliminación), y en ejecución en un entorno de un solo hilo, puede considerar usar StringBuilder, como el ensamblaje de la declaración SQL, la encapsulación JSON, etc.
Iii. Análisis
En pocas palabras, la principal diferencia de rendimiento entre el tipo de cadena y el tipo de stringbuffer es en realidad que la cadena es un objeto inmutable, por lo que cada vez que cambia el tipo de cadena, en realidad es equivalente a generar un nuevo objeto de cadena y luego apuntar el puntero al nuevo objeto de cadena. Por lo tanto, es mejor no usar cadenas para cadenas que cambien con frecuencia, porque cada generación de objetos tendrá un impacto en el rendimiento del sistema, especialmente cuando hay demasiados objetos referenciados en la memoria, el GC del JVM comenzará a funcionar, lo que definitivamente será bastante lento.
Si usa la clase StringBuffer, el resultado será diferente. Cada vez que el resultado funcionará en el objeto StringBuffer en sí, en lugar de generar un nuevo objeto y luego cambiar la referencia del objeto. Por lo tanto, en general, recomendamos usar StringBuffer, especialmente cuando los objetos de cadena a menudo se cambian. En algunos casos especiales, el empalme de los objetos de cadena es realmente interpretado por el JVM como un empalme de los objetos StringBuffer. Por lo tanto, la velocidad de los objetos de cadena no es más lenta que los objetos StringBuffer en estos momentos. Especialmente en la siguiente generación de objetos de cadena, la eficiencia de la cadena es mucho más rápida que StringBuffer:
String S1 = "Esto es solo una" + "simple" + "prueba"; StringBuffer sb = new StringBuilder ("Esto es solo un"). Append ("simple"). Append ("Test");Se sorprenderá al descubrir que la velocidad de generar objetos S1 String es simplemente demasiado rápida, y en este momento, StringBuffer no tiene ninguna ventaja en la velocidad. De hecho, este es un truco del JVM. En los ojos de la JVM, esto
Cadena s1 = "Esto es solo una" + "simple" + "prueba";
De hecho:
String S1 = "Esta es solo una prueba simple";
Entonces, por supuesto, no lleva mucho tiempo. Pero lo que debe tener en cuenta aquí es que si su cadena proviene de otro objeto de cadena, la velocidad no es tan rápida, por ejemplo:
String S2 = "Esto es solo un"; String S3 = "Simple"; String S4 = "Test"; String S1 = S2 + S3 + S4;
En este momento, el JVM lo hará de manera regular de la manera original.
4. Optimización y procesamiento en profundidad de JVM
¿Existe realmente el costo de rendimiento anterior? El empalme de cadenas es tan común, ¿no hay una optimización de procesamiento especial? La respuesta es que esta optimización se realiza al compilar .java al bytecode en JVM.
Si un programa Java quiere ejecutarse, tomará dos períodos, compilará tiempo y tiempo de ejecución. En el momento de la compilación, el Java JVM (compilador) convierte el archivo Java en Bytecode. En tiempo de ejecución, la máquina virtual Java (JVM) ejecuta el tiempo de compilación generado por el código de bytecode. A través de estos dos períodos, Java ha logrado la llamada compilación y corriendo en todas partes.
Experimentemos con qué optimizaciones se hicieron durante el período de compilación, y creamos un código que puede tener costos de rendimiento.
Concatenación de clase pública {public static void main (String [] args) {String UserName = "Andy"; String Age = "24"; String Job = "Developer"; Info de cadena = nombre de usuario + edad + trabajo; System.out.println (info); }}Compilar concatenation.java. Obtenga concatenation.class
Concatenación de Javac.java
Luego usamos Javap para descompilar el archivo de concatenation.class compilado. Javap -C Concatenation. Si no se encuentra el comando Javap, considere agregar el directorio donde el Javap se encuentra a la variable de entorno o usando la ruta completa de Javap.
17: 22: 04 -androidyue ~/workspace_adt/strings/src $ javap -c concatenation compilado de "concatenation.java" concatenación de clase pública {concatenación pública (); Código: 0: Aload_0 1: Invokespecial #1 // Method java/lang/object. "<Init>" :() v 4: return public static void main (java.lang.string []); Código: 0: LDC #2 // String Andy 2: Store_1 3: LDC #3 // String 24 5: Store_2 6: LDC #4 // String Developer 8: Store_3 9: New #5 // class java/lang/stringBuilder 12: dup 13: Invokespecial #6 // método java/lang/stringbuilder. "<<th inicio>") V 16) InvokeVirtual #7 // método java/lang/stringBuilder.append: (ljava/lang/string;) ljava/lang/stringBuilder; 20: Aload_2 21: InvokeVirtual #7 // método java/lang/stringBuilder.append: (ljava/lang/string;) ljava/lang/stringBuilder; 24: Aload_3 25: InvokeVirtual #7 // método java/lang/stringBuilder.append: (ljava/lang/string;) ljava/lang/stringBuilder; 28: InvokeVirtual #8 // método java/lang/stringBuilder.ToString :() ljava/lang/string; 31: Store 4 33: Getstatic #9 // Field java/lang/system.out: ljava/io/printstream; 36: Aload 4 38: InvokeVirtual #10 // Método Java/io/printstream.println: (ljava/lang/string;) v 41: return}Entre ellos, LDC, Store, etc. se encuentran las instrucciones de Bytecode Java, similares a las instrucciones de ensamblaje. Los siguientes comentarios usan contenido relacionado con Java para explicación. Podemos ver que hay muchos StringBuilders en él, pero los llamamos en código Java sin visualización. Esta es la optimización realizada por Javajvm. Cuando Javajvm encuentra el empalme de cadena, se creará un objeto StringBuilder. El empalme detrás de esto en realidad está llamando al método de adición del objeto StringBuilder. De esta manera, no habrá problemas que nos preocupe.
5. ¿Confiar solo en la optimización de JVM?
Dado que el JVM nos ha ayudado a optimizar, ¿es suficiente para confiar únicamente en la optimización de JVM? Por supuesto que no.
Veamos un código que no ha sido optimizado para un bajo rendimiento
public void implicituseStringBuilder (String [] valores) {String result = ""; for (int i = 0; i <valores.length; i ++) {result += valores [i]; } System.out.println (resultado);}Compilar con Javac y ver con Javap
Public void implicitusEstringBuilder (java.lang.string []); Código: 0: LDC #11 // cadena 2: store_2 3: iconst_0 4: istore_3 5: iload_3 6: Aload_1 7: ArrayLength 8: if_icmpge 38 11: nuevo #5 // class java/lang/stringBuilder 14: dup 15: invocial #6 // método java/lang/string builder. 18: Aload_2 19: InvokeVirtual #7 // método java/lang/stringBuilder.append: (ljava/lang/string;) ljava/lang/stringBuilder; 22: Aload_1 23: ILOAD_3 24: Aaload 25: InvokeVirtual #7 // Método Java/lang/StringBuilder.append: (ljava/lang/string;) ljava/lang/stringBuilder; 28: InvokeVirtual #8 // método java/lang/stringBuilder.ToString :() ljava/lang/string; 31: Store_2 32: Iinc 3, 1 35: GoTo 5 38: Getstatic #9 // Field java/lang/system.out: ljava/io/printstream; 41: Aload_2 42: InvokeVirtual #10 // Método java/io/printstream.println: (ljava/lang/string;) v 45: return
Entre ellos 8: IF_ICMPGE 38 y 35: GOTO 5 Forma un bucle. 8: IF_ICMPGE 38 significa que si la comparación entera de la pila de operando JVM es mayor o igual a (i <el resultado opuesto de valores.length), saltará a la línea 38 (System.out). 35: GoTo 5 significa saltar directamente a la línea 5.
Pero aquí hay algo muy importante que la creación de objetos StringBuilder ocurre entre los bucles, lo que significa que cuántas veces los bucles crearán múltiples objetos StringBuilder, lo que obviamente no es bueno. Código desnudo de bajo nivel.
Optimice ligeramente para mejorar instantáneamente la calidad.
public void ExplicitUseCringBuilder (String [] valores) {stringBuilder result = new StringBuilder (); for (int i = 0; i <value.length; i ++) {result.append (valores [i]); }}Información compilada correspondiente
11: Aload_1 12: ArrayLength 13: if_icmpge 30 16: Aload_2 17: Aload_1 18: ILOAD_3 19: AALOAD 20: InvokeVirtual #7 // Método Java/Lang/StringBuilder.append: (LJAVA/Lang/String;) LJAVA/LANG/STRINGBUIRDER; 23: Pop 24: Iinc 3, 1 27: GoTo 10 30: Regreso
Como se puede ver desde arriba, 13: IF_ICMPGE 30 y 27: GOTO 10 Forma un bucle de bucle, mientras que 0: el nuevo #5 está fuera del bucle, por lo que StringBuilder no se crea varias veces.
En general, debemos tratar de evitar la creación implícita o explícita de StringBuilders en el cuerpo del bucle. Por lo tanto, aquellos que entienden cómo se compila el código y cómo se ejecuta internamente tienen niveles de código relativamente altos.
6. Conclusión
En la mayoría de los casos, StringBuffer> String
Java.lang.stringbuffer es una secuencia segura de hilo de caracteres variables. Un búfer de cadena en forma de cadena, pero no se puede modificar. Aunque contiene una cierta secuencia de caracteres en cualquier momento, la longitud y el contenido de la secuencia pueden cambiarse mediante algunas llamadas de método. Los búferes de cadena se pueden usar de manera segura en programas para múltiples lecturas. Y estos métodos se pueden sincronizar cuando es necesario, por lo que todas las operaciones en cualquier instancia particular parecen ocurrir en un orden en serie que sea consistente con el orden de las llamadas de método realizadas por cada hilo involucrado.
Las operaciones principales en StringBuffer son los métodos de adjunto e inserto, que se pueden sobrecargar para aceptar cualquier tipo de datos. Cada método puede convertir efectivamente los datos dados en una cadena, y luego agregar o insertar los caracteres de esa cadena en el búfer de cadena. El método de agregado siempre agrega estos caracteres al final del búfer; El método de inserción agrega caracteres en el punto especificado.
Por ejemplo, si Z se refiere a un objeto de búfer de cadena cuyo contenido actual es "inicio", esta llamada de método Z.append ("le") hará que el búfer de cadena contenga "asustar" (acumulado); y Z.Insert (4, "LE") cambiará el búfer de cadena para contener "Starlet".
En la mayoría de los casos, StringBuilder> StringBuffer
java.lang.stringbuilder Una secuencia de caracteres variable es nueva en Java 5.0. Esta clase proporciona una API compatible con StringBuffer, pero no se garantiza que se sincronice, por lo que el escenario de uso es de un solo rostro. Esta clase está diseñada para ser un simple reemplazo para StringBuffer, cuando los búferes de cadena son utilizados por un solo hilo (esto es común). Si es posible, se recomienda tomar esta clase primero, ya que es más rápido que StringBuffer en la mayoría de las implementaciones. Los métodos de uso de ambos son básicamente los mismos.