String, StringBuffer y StringBuilder

Respuestas:

378

Diferencia de mutabilidad:

Stringes inmutable , si intenta alterar sus valores, otro objeto se crea, mientras que StringBuffery StringBuilderson mutables para que puedan cambiar sus valores.

Diferencia de seguridad del hilo:

La diferencia entre StringBuffery StringBuilderes que StringBufferes segura para subprocesos. Entonces, cuando la aplicación necesita ejecutarse solo en un solo hilo, entonces es mejor usarla StringBuilder. StringBuilderes más eficiente que StringBuffer.

Situaciones

  • Si su cadena no va a cambiar, use una clase de cadena porque un Stringobjeto es inmutable.
  • Si su cadena puede cambiar (ejemplo: mucha lógica y operaciones en la construcción de la cadena) y solo se accederá desde un solo hilo, usar a StringBuilderes lo suficientemente bueno.
  • Si su cadena puede cambiar, y se accederá desde varios subprocesos, use un StringBufferporque StringBufferes sincrónico para que tenga seguridad de subprocesos.
bakkal
fuente
16
Además, el uso de String para operaciones lógicas es bastante lento y no se recomienda en absoluto, ya que JVM convierte el String en un StringBuffer en el código de bytes. Se desperdicia una gran cantidad de gastos generales al convertir de String a StringBuffer y luego nuevamente a String.
Pieter van Niekerk
2
Entonces, Stringscuando modificamos el valor, se crea otro objeto. ¿La referencia de objeto anterior se anula para que pueda ser recolectada por la basura GCo incluso se recolecta basura?
Harsh Vardhan
@PietervanNiekerk ¿Qué quieres decir con operaciones lógicas?
emlai
Me refiero a las operaciones lógicas que son básicas. Ahora, una cosa que me gustaría preguntar, como lo dice @Peter, ¿deberíamos comenzar a usar StringBuffer en lugar de String en todos los casos o hay algunos casos específicos?
JavaDragon
@bakkal ¿Puedo usar todos los métodos de Stringwith StringBuilder?
roottraveller
47
  • Usas Stringcuando una estructura inmutable es apropiada; obtener una nueva secuencia de caracteres de a Stringpuede llevar a una penalización de rendimiento inaceptable, ya sea en tiempo de CPU o memoria (obtener subcadenas es eficiente de CPU porque los datos no se copian, pero esto significa que una cantidad de datos potencialmente mucho mayor puede permanecer asignada).
  • Se usa StringBuildercuando necesita crear una secuencia de caracteres mutable, generalmente para concatenar varias secuencias de caracteres juntas.
  • Se usa StringBufferen las mismas circunstancias que se usaría StringBuilder, pero cuando los cambios en la cadena subyacente se deben sincronizar (porque varios subprocesos están leyendo / modificando el búfer de cadena).

Vea un ejemplo aquí .

Artefacto
fuente
2
conciso, pero incompleto, pierde la razón fundamental para usar StringBuilder / Buffer y es reducir o eliminar la reasignación y la copia de matriz del comportamiento de concatenación de String normal.
1
"Utiliza String cuando se trata de cadenas inmutables", no tiene sentido. Las instancias de String SON inmutables, por lo que quizás el comentario debería leer "Use String cuando el uso de memoria debido a la inmutabilidad no importa". La respuesta aceptada cubre sus bases bastante bien.
fooMonster
28

Los basicos:

Stringes una clase inmutable, no se puede cambiar. StringBuilderes una clase mutable a la que se puede agregar, reemplazar o eliminar caracteres y, en última instancia, convertir a String StringBufferes la versión original sincronizada deStringBuilder

Debería preferir StringBuilderen todos los casos en los que solo tiene un único hilo accediendo a su objeto.

Los detalles:

También tenga en cuenta que StringBuilder/Buffersno son mágicos, solo usan una matriz como objeto de respaldo y que la matriz debe reasignarse cada vez que se llena. Asegúrate de crear tus StringBuilder/Bufferobjetos lo suficientemente grandes originalmente donde no tengan que volver a dimensionarse constantemente cada vez que .append()se los llama.

El cambio de tamaño puede ser muy degenerado. Básicamente cambia el tamaño de la matriz de respaldo a 2 veces su tamaño actual cada vez que necesita expandirse. Esto puede provocar que se asignen grandes cantidades de RAM y que no se usen cuando las StringBuilder/Bufferclases comienzan a crecer.

En Java String x = "A" + "B";utiliza un StringBuilderdetrás de escena. Entonces, para casos simples, no hay beneficio de declarar el suyo propio. Pero si está construyendo Stringobjetos que son grandes, digamos menos de 4k, entonces declarar StringBuilder sb = StringBuilder(4096);es mucho más eficiente que la concatenación o usar el constructor predeterminado que tiene solo 16 caracteres. Si Stringva a tener menos de 10k, inicialícelo con el constructor a 10k para estar seguro. Pero si se inicializa a 10k, entonces escribe 1 carácter más de 10k, se reasignará y copiará en una matriz de 20k. Por lo tanto, inicializar alto es mejor que bajo.

En el caso de cambio de tamaño automático, en el 17º carácter, la matriz de respaldo se reasigna y se copia a 32 caracteres, en el 33º carácter esto vuelve a ocurrir y se puede reasignar y copiar la matriz en 64 caracteres. Puede ver cómo esto degenera en muchas reasignaciones y copias, que es lo que realmente está tratando de evitar usar StringBuilder/Bufferen primer lugar.

Esto es del código fuente de JDK 6 para AbstractStringBuilder

   void expandCapacity(int minimumCapacity) {
    int newCapacity = (value.length + 1) * 2;
        if (newCapacity < 0) {
            newCapacity = Integer.MAX_VALUE;
        } else if (minimumCapacity > newCapacity) {
        newCapacity = minimumCapacity;
    }
        value = Arrays.copyOf(value, newCapacity);
    }

Una mejor práctica es inicializar StringBuilder/Bufferun poco más de lo que crees que vas a necesitar si no sabes de inmediato qué tan grande Stringserá, pero puedes adivinar. Una asignación de un poco más de memoria de la que necesita será mejor que muchas reasignaciones y copias.

También tenga cuidado de inicializar a StringBuilder/Buffercon a, Stringya que solo asignará el tamaño de los caracteres de Cadena + 16, que en la mayoría de los casos solo comenzará el ciclo de reasignación y copia degenerado que está tratando de evitar. Lo siguiente es directamente del código fuente de Java 6.

public StringBuilder(String str) {
    super(str.length() + 16);
    append(str);
    }

Si por casualidad terminas con una instancia de StringBuilder/Buffereso que no creaste y no puedes controlar el constructor que se llama, hay una manera de evitar el comportamiento degenerado de reasignación y copia. Llame .ensureCapacity()con el tamaño que desea para asegurarse de que su resultado Stringse ajuste.

Las alternativas

Solo como una nota, si está haciendo una String construcción y manipulación realmente pesadas , hay una alternativa mucho más orientada al rendimiento llamada Ropes .

Otra alternativa es crear una StringListimplementación subclasificando ArrayList<String>y agregando contadores para rastrear el número de caracteres en todas .append()y cada una de las operaciones de mutación de la lista, luego anular .toString()para crear uno StringBuilderdel tamaño exacto que necesita y recorrer la lista y construir en la salida, incluso puede convertir esa StringBuildervariable en una instancia y 'almacenar en caché' los resultados .toString()y solo tiene que volver a generarla cuando algo cambia.

Tampoco se olvide de String.format()crear un formato de salida fijo, que el compilador puede optimizar a medida que lo mejoran.

usuario177800
fuente
1
¿ String x = "A" + "B";Realmente se compila para ser un StringBuilder? ¿Por qué no solo compilaría? String x = "AB";Solo debería usar un StringBuilder si los componentes no se conocen en el momento de la compilación.
Matt Greer
podría optimizar las constantes de String, no puedo recordar desde la última vez que descompiló el código de bytes, pero sí sé que si hay alguna variable allí, seguramente usará una implementación de StringBuilder. Puede descargar el código fuente JDK y descubrirlo usted mismo. "A" + "B" es un ejemplo artificial seguro.
Me preguntaba sobre String.format (). Nunca lo he visto realmente utilizado en proyectos. Por lo general, es el StringBuilder. Bueno, por lo general es "A" + "B" + "C" porque la gente es perezosa;) solía usar siempre un StringBuilder, incluso si solo se concatenaban dos cadenas, porque en el futuro, tal vez se agregarían más cadenas . Nunca usé String.format () principalmente porque nunca recordé qué JDK se introdujo; veo que es JDK1.5, lo usaría a favor de las otras opciones.
jamiebarrow
9

¿Te refieres a concatenación?

Ejemplo del mundo real: desea crear una nueva cadena a partir de muchos otros .

Por ejemplo para enviar un mensaje:

Cuerda

String s = "Dear " + user.name + "<br>" + 
" I saw your profile and got interested in you.<br>" +
" I'm  " + user.age + "yrs. old too"

StringBuilder

String s = new StringBuilder().append.("Dear ").append( user.name ).append( "<br>" ) 
          .append(" I saw your profile and got interested in you.<br>") 
          .append(" I'm  " ).append( user.age ).append( "yrs. old too")
          .toString()

O

String s = new StringBuilder(100).appe..... etc. ...
// The difference is a size of 100 will be allocated upfront as  fuzzy lollipop points out.

StringBuffer (la sintaxis es exactamente como con StringBuilder, los efectos difieren)

Acerca de

StringBuffer vs. StringBuilder

El primero está sincronizado y luego no.

Entonces, si lo invoca varias veces en un solo hilo (que es el 90% de los casos), StringBuilderse ejecutará mucho más rápido porque no se detendrá para ver si posee el bloqueo del hilo.

Por lo tanto, es recomendable usarlo StringBuilder(a menos que, por supuesto, tenga más de un hilo accediendo al mismo tiempo, lo cual es raro)

Stringel compilador puede optimizar la concatenación ( usando el operador + ) para usar StringBuilderdebajo, por lo que ya no es algo de qué preocuparse, en los días antiguos de Java, esto era algo que todo el mundo dice que debe evitarse a toda costa, porque cada concatenación creó un nuevo objeto String. Los compiladores modernos ya no hacen esto, pero aún así es una buena práctica usarlo StringBuilderen caso de que use un compilador "antiguo".

editar

Solo para quién tiene curiosidad, esto es lo que hace el compilador para esta clase:

class StringConcatenation {
    int x;
    String literal = "Value is" + x;
    String builder = new StringBuilder().append("Value is").append(x).toString();
}

javap -c StringConcatenation

Compiled from "StringConcatenation.java"
class StringConcatenation extends java.lang.Object{
int x;

java.lang.String literal;

java.lang.String builder;

StringConcatenation();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   new #2; //class java/lang/StringBuilder
   8:   dup
   9:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   12:  ldc #4; //String Value is
   14:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   17:  aload_0
   18:  getfield    #6; //Field x:I
   21:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   24:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   27:  putfield    #9; //Field literal:Ljava/lang/String;
   30:  aload_0
   31:  new #2; //class java/lang/StringBuilder
   34:  dup
   35:  invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   38:  ldc #4; //String Value is
   40:  invokevirtual   #5; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   43:  aload_0
   44:  getfield    #6; //Field x:I
   47:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   50:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   53:  putfield    #10; //Field builder:Ljava/lang/String;
   56:  return

}

Las líneas numeradas del 5 al 27 son para la cadena llamada "literal"

Las líneas numeradas del 31 al 53 son para la cadena denominada "constructor"

No hay diferencia, exactamente el mismo código se ejecuta para ambas cadenas.

OscarRyz
fuente
2
Este es un muy mal ejemplo. Inicializar StringBuilder con "Estimado" significa que el primer .append () causará una reasignación y copia. Negando completamente cualquier eficiencia que intente obtener sobre la concatenación "normal". Un mejor ejemplo sería crearlo con un tamaño inicial que contendrá todo el contenido de la Cadena final.
1
En general, NO es una buena práctica utilizar una StringBuilderconcatenación de cadenas para hacer en el lado derecho de la tarea. Cualquier buena implementación utilizará un StringBuilder detrás de escena como usted dice. Además, su ejemplo de "a" + "b"se compilaría en un solo literal, "ab"pero si lo usara, StringBuilderse generarían dos llamadas innecesarias a append().
Mark Peters
@ Mark No quise usarlo, "a"+"b"pero para decir qué era una concatenación de String, lo cambio para que sea explícito. Lo que no dice es por qué no es una buena práctica hacerlo. Eso es exactamente lo que hace un compilador (moderno). @fuzzy, estoy de acuerdo, especialmente cuando sabes cuál sería el tamaño de la cadena final (aprox.).
OscarRyz
1
No creo que sea una práctica particularmente mala , pero ciertamente no quisiera ninguna recomendación para hacer eso en general. Es torpe y difícil de leer, y demasiado detallado. Además, fomenta errores como el tuyo en el que estás separando dos literales que de otro modo podrían compilarse como uno solo. Solo si el perfil me dijera que marca la diferencia, lo haría a tu manera.
Mark Peters
@ Mark Lo tengo. Estaba pensando más en el "gran trozo de código como una plantilla" y no en cada literal de cadena normal. Pero sí, estoy de acuerdo, ya que hoy en día hacen lo mismo, no tiene sentido (hace 10 años era una razón para rechazar un cambio en una revisión del código) :)
OscarRyz
8
-------------------------------------------------- --------------------------------
                  String StringBuffer StringBuilder
-------------------------------------------------- --------------------------------                 
Área de almacenamiento | Montón de agrupación de cadenas constantes
Modificable | No (inmutable) Sí (mutable) Sí (mutable)
Hilo seguro | Si si no
 Rendimiento | Rápido Muy lento Rápido
-------------------------------------------------- --------------------------------
Abhijit Maity
fuente
¿Por qué el rendimiento de String es rápido y el rendimiento de StringBuffer es muy lento?
gaurav
1
@gaurav puedes leer su código fuente, todos los métodos en StringBuffer son synchronisedy esa es la razón .
Hearen
8

Familia de cuerdas

Cuerda

El String classrepresenta cadenas de caracteres. Todos los literales de cadena en el programa Java, como "abc"se implementan como instancias de esta clase.

Los objetos de cadena son inmutables una vez que se crean, no podemos cambiarlos. (Las cadenas son constantes )

  • Si se crea una cadena utilizando el constructor o el método a continuación, se almacenarán las cadenas en memoria de pila , así como SringConstantPool. Pero antes de guardar en el grupo, invoca el intern()método para verificar la disponibilidad de objetos con el mismo contenido en el grupo usando el método igual. Si String-copy está disponible en el Pool, entonces devuelve la referencia. De lo contrario, el objeto String se agrega al grupo y devuelve la referencia.

    • El lenguaje Java proporciona soporte especial para el operador de concatenación de cadenas ( +) y para la conversión de otros objetos a cadenas. La concatenación de cadenas se implementa a través de la clase StringBuilder (o StringBuffer) y su método append.

    String heapSCP = new String("Yash");
    heapSCP.concat(".");
    heapSCP = heapSCP + "M";
    heapSCP = heapSCP + 777;
    
    // For Example: String Source Code 
    public String concat(String str) {
        int otherLen = str.length();
        if (otherLen == 0) {
            return this;
        }
        int len = value.length;
        char buf[] = Arrays.copyOf(value, len + otherLen);
        str.getChars(buf, len);
        return new String(buf, true);
    }
  • Los literales de cadena se almacenan en StringConstantPool.

    String onlyPool = "Yash";

StringBuilder y StringBuffer son secuencias de caracteres mutables. Eso significa que uno puede cambiar el valor de estos objetos. StringBuffer tiene los mismos métodos que StringBuilder, pero cada método en StringBuffer está sincronizado, por lo que es seguro para subprocesos.

  • Los datos de StringBuffer y StringBuilder solo se pueden crear con un nuevo operador. Por lo tanto, se almacenan en la memoria del montón.

  • Las instancias de StringBuilder no son seguras para su uso por varios subprocesos. Si se requiere dicha sincronización, se recomienda utilizar StringBuffer.

    StringBuffer threadSafe = new StringBuffer("Yash");
    threadSafe.append(".M");
    threadSafe.toString();
    
    StringBuilder nonSync = new StringBuilder("Yash");
    nonSync.append(".M");
    nonSync.toString();
  • StringBuffer y StringBuilder están teniendo un métodos especiales como., replace(int start, int end, String str)Y reverse().

    NOTA : StringBuffer y SringBuilder son mutables ya que proporcionan la implementación de Appendable Interface.


Cuándo usar cuál.

  • Si no va a cambiar el valor cada vez, es mejor usarlo String Class. Como parte de Generics, si desea ordenar Comparable<T>o comparar valores, elija String Class.

    //ClassCastException: java.lang.StringBuffer cannot be cast to java.lang.Comparable
    Set<StringBuffer> set = new TreeSet<StringBuffer>();
    set.add( threadSafe );
    System.out.println("Set : "+ set);
  • Si va a modificar el valor cada vez que vaya a StringBuilder, que es más rápido que StringBuffer. Si varios hilos están modificando el valor, vaya a StringBuffer.

Yash
fuente
4

Además, StringBufferes seguro para subprocesos, que StringBuilderno lo es.

Por lo tanto, en una situación en tiempo real cuando diferentes subprocesos acceden a él, StringBuilderpodría tener un resultado indeterminado.

Lars Andren
fuente
3

Tenga en cuenta que si está utilizando Java 5 o más reciente, debe usarlo en StringBuilderlugar de StringBuffer. De la documentación de la API:

Como de la liberación de JDK 5, esta clase se ha complementado con una clase equivalente diseñado para su uso por un solo hilo, StringBuilder. La StringBuilderclase generalmente se debe usar con preferencia a esta, ya que admite todas las mismas operaciones pero es más rápida, ya que no realiza sincronización.

En la práctica, casi nunca usará esto de varios subprocesos al mismo tiempo, por lo que la sincronización StringBufferes casi siempre innecesaria.

Jesper
fuente
3

Personalmente, no creo que haya un uso real en el mundo StringBuffer. ¿Cuándo querría comunicarme entre múltiples hilos manipulando una secuencia de caracteres? Eso no suena útil en absoluto, pero tal vez aún no haya visto la luz :)

flujo libre
fuente
3

La diferencia entre String y las otras dos clases es que String es inmutable y las otras dos son clases mutables.

¿Pero por qué tenemos dos clases con el mismo propósito?

La razón es que StringBufferes seguro para subprocesos y StringBuilderno lo es. StringBuilderes una nueva clase StringBuffer Apiy se introdujo JDK5y siempre se recomienda si está trabajando en un entorno de un solo subproceso, ya que es muchoFaster

Para obtener detalles completos, puede leer http://www.codingeek.com/java/stringbuilder-and-stringbuffer-a-way-to-create-mutable-strings-in-java/

Hitesh Garg
fuente
2

En java, String es inmutable. Al ser inmutable, queremos decir que una vez que se crea una Cadena, no podemos cambiar su valor. StringBuffer es mutable. Una vez que se crea un objeto StringBuffer, simplemente agregamos el contenido al valor del objeto en lugar de crear un nuevo objeto. StringBuilder es similar a StringBuffer pero no es seguro para subprocesos. Los métodos de StingBuilder no están sincronizados, pero en comparación con otras cadenas, Stringbuilder se ejecuta más rápido. Puede aprender la diferencia entre String, StringBuilder y StringBuffer implementándolos.

rajat ghai
fuente