¿Por qué String es inmutable en Java?

78

No pude entender la razón de esto. Siempre uso la clase String como otros desarrolladores, pero cuando modifico el valor de la misma, se crea una nueva instancia de String.

¿Cuál podría ser la razón de la inmutabilidad de la clase String en Java?

Sé que hay algunas alternativas como StringBuffer o StringBuilder. Es solo curiosidad.

yfklon
fuente
20
Técnicamente, no es un duplicado, pero Eric Lippert da una gran respuesta a esta pregunta aquí: programmers.stackexchange.com/a/190913/33843
Heinzi

Respuestas:

105

Concurrencia

Java se definió desde el principio con consideraciones de concurrencia. Como se ha mencionado a menudo, las mutables compartidas son problemáticas. Una cosa puede cambiar a otra detrás de otro hilo sin que ese hilo sea consciente de ello.

Hay una gran cantidad de errores de C ++ multiproceso que se han creado debido a una cadena compartida, donde un módulo pensó que era seguro cambiar cuando otro módulo en el código había guardado un puntero y esperaba que permaneciera igual.

La 'solución' a esto es que cada clase hace una copia defensiva de los objetos mutables que se le pasan. Para cadenas mutables, esto es O (n) para hacer la copia. Para cadenas inmutables, hacer una copia es O (1) porque no es una copia, es el mismo objeto que no puede cambiar.

En un entorno multiproceso, los objetos inmutables siempre se pueden compartir de forma segura entre sí. Esto conduce a una reducción general en el uso de memoria y mejora el almacenamiento en memoria caché.

Seguridad

Muchas veces las cadenas se pasan como argumentos a los constructores: las conexiones de red y los protocolos son los dos que más fácilmente se nos ocurren. Ser capaz de cambiar esto en un momento indeterminado más adelante en la ejecución puede conducir a problemas de seguridad (la función pensó que se estaba conectando a una máquina, pero se desvió a otra, pero todo en el objeto parece estar conectado a la primera ... es incluso la misma cadena).

Java permite usar la reflexión, y los parámetros para esto son cadenas. El peligro de que alguien pase una cadena que puede modificarse a otro método que refleja. Esto es muy malo.

Claves para el hash

La tabla hash es una de las estructuras de datos más utilizadas. Las claves de la estructura de datos suelen ser cadenas. Tener cadenas inmutables significa que (como arriba) la tabla hash no necesita hacer una copia de la clave hash cada vez. Si las cadenas fueran mutables, y la tabla hash no hiciera esto, sería posible que algo cambiara la clave hash a distancia.

La forma en que funciona el Object en java es que todo tiene una clave hash (a la que se accede mediante el método hashCode ()). Tener una cadena inmutable significa que el código hash se puede almacenar en caché. Teniendo en cuenta la frecuencia con la que las cadenas se usan como claves para un hash, esto proporciona un aumento significativo del rendimiento (en lugar de tener que volver a calcular el código hash cada vez).

Subcadenas

Al hacer que la cadena sea inmutable, la matriz de caracteres subyacente que respalda la estructura de datos también es inmutable. Esto permite ciertas optimizaciones en el substringmétodo que se debe hacer (no necesariamente se hace, también presenta la posibilidad de algunas pérdidas de memoria también).

Si lo haces:

String foo = "smiles";
String bar = foo.substring(1,5);

El valor de bares 'milla'. Sin embargo, tanto fooy barpueda ir acompañado de la misma matriz de caracteres, lo que reduce la creación de instancias de más matrices de caracteres o copiarlo - simplemente utilizando diferentes puntos de inicio y fin dentro de la cadena.

foo | El | (0, 6)
    vv
    sonrisas
     ^ ^
bar | El | (15)

Ahora, la desventaja de eso (la pérdida de memoria) es que si uno tuviera una cadena de 1k de largo y tomara la subcadena del primer y segundo carácter, también estaría respaldada por la matriz de caracteres de 1k de largo. Esta matriz permanecería en la memoria incluso si la cadena original que tenía un valor de la matriz de caracteres completa se recolectara basura.

Uno puede ver esto en String from JDK 6b14 (el siguiente código es de una fuente GPL v2 y se usa como ejemplo)

   public String(char value[], int offset, int count) {
       if (offset < 0) {
           throw new StringIndexOutOfBoundsException(offset);
       }
       if (count < 0) {
           throw new StringIndexOutOfBoundsException(count);
       }
       // Note: offset or count might be near -1>>>1.
       if (offset > value.length - count) {
           throw new StringIndexOutOfBoundsException(offset + count);
       }
       this.offset = 0;
       this.count = count;
       this.value = Arrays.copyOfRange(value, offset, offset+count);
   }

   // Package private constructor which shares value array for speed.
   String(int offset, int count, char value[]) {
       this.value = value;
       this.offset = offset;
       this.count = count;
   }

   public String substring(int beginIndex, int endIndex) {
       if (beginIndex < 0) {
           throw new StringIndexOutOfBoundsException(beginIndex);
       }
       if (endIndex > count) {
           throw new StringIndexOutOfBoundsException(endIndex);
       }
       if (beginIndex > endIndex) {
           throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
       }
       return ((beginIndex == 0) && (endIndex == count)) ? this :
           new String(offset + beginIndex, endIndex - beginIndex, value);
   }

Observe cómo la subcadena utiliza el constructor de cadenas de nivel de paquete que no implica ninguna copia de la matriz y sería mucho más rápido (a expensas de posiblemente mantener algunas matrices grandes, aunque tampoco duplicar matrices grandes).

Tenga en cuenta que el código anterior es para Java 1.6. La forma en que se implementa el constructor de subcadenas se modificó con Java 1.7, tal como se documenta en la representación interna Cambios en la cadena realizada en Java 1.7.0_06 , el problema relacionado con la pérdida de memoria que mencioné anteriormente. Es probable que Java no se haya visto como un lenguaje con mucha manipulación de cadenas, por lo que el aumento de rendimiento para una subcadena fue algo bueno. Ahora, con enormes documentos XML almacenados en cadenas que nunca se recopilan, esto se convierte en un problema ... y, por lo tanto, el cambio a Stringno usar la misma matriz subyacente con una subcadena, para que la matriz de caracteres más grande se pueda recopilar más rápidamente.

No abuses de la pila

Se podría pasar el valor de la cadena en lugar de la referencia a la cadena inmutable para evitar problemas con la mutabilidad. Sin embargo, con cadenas grandes, pasar esto en la pila sería ... abusivo para el sistema (colocar documentos xml completos como cadenas en la pila y luego quitarlos o continuar pasándolos ...).

La posibilidad de deduplicación

De acuerdo, esto no fue una motivación inicial de por qué las cadenas deberían ser inmutables, pero cuando uno está mirando la razón de por qué las cadenas inmutables son algo bueno, esto es ciertamente algo a considerar.

Cualquiera que haya trabajado un poco con Strings sabe que puede succionar memoria. Esto es especialmente cierto cuando estás haciendo cosas como extraer datos de bases de datos que se quedan por un tiempo. Muchas veces con estas picaduras, son la misma cadena una y otra vez (una vez para cada fila).

Actualmente, muchas aplicaciones Java a gran escala tienen cuellos de botella en la memoria. Las mediciones han demostrado que aproximadamente el 25% del conjunto de datos dinámicos de almacenamiento dinámico de Java en este tipo de aplicaciones es consumido por objetos String. Además, aproximadamente la mitad de esos objetos String son duplicados, donde duplicados significa string1.equals (string2) es verdadero. Tener duplicados objetos String en el montón es, esencialmente, solo un desperdicio de memoria. ...

Con Java 8 actualización 20, JEP 192 (motivación citada anteriormente) se está implementando para abordar esto. Sin entrar en detalles sobre cómo funciona la deduplicación de cadenas, es esencial que las cadenas mismas sean inmutables. No puede deduplicar StringBuilders porque pueden cambiar y no desea que alguien cambie algo debajo de usted. Las cadenas inmutables (relacionadas con ese grupo de cadenas) significa que puede pasar y si encuentra dos cadenas que son iguales, puede apuntar una referencia de cadena a la otra y dejar que el recolector de basura consuma el nuevo no utilizado.

Otros idiomas

El objetivo C (que precede a Java) tiene NSStringy NSMutableString.

C # y .NET tomaron las mismas opciones de diseño de la cadena predeterminada, que es inmutable.

Las cuerdas de Lua también son inmutables.

Python también.

Históricamente, Lisp, Scheme, Smalltalk todos internan la cadena y, por lo tanto, hacen que sea inmutable. Los lenguajes dinámicos más modernos a menudo usan cadenas de alguna manera que requiere que sean inmutables (puede que no sea una cadena , pero es inmutable).

Conclusión

Estas consideraciones de diseño se han hecho una y otra vez en una multitud de idiomas. Es el consenso general de que las cadenas inmutables, a pesar de su incomodidad, son mejores que las alternativas y conducen a un mejor código (menos errores) y ejecutables más rápidos en general.


fuente
3
Java proporciona cadenas mutables e inmutables. Esta respuesta detalla algunas ventajas de rendimiento que se pueden hacer en cadenas inmutables, y algunas razones por las que uno podría elegir datos inmutables; pero no discute por qué la versión inmutable es la versión predeterminada.
Billy ONeal
3
@BillyONeal: un valor predeterminado seguro y una alternativa insegura casi siempre conduce a sistemas más seguros que el enfoque opuesto.
Joachim Sauer
44
@BillyONeal Si lo inmutable no fuera el predeterminado, los problemas de concurrencia, seguridad y hash serían más comunes. Los diseñadores de lenguaje han elegido (en parte en respuesta a C) crear un lenguaje donde se configuren los valores predeterminados para tratar de evitar una serie de errores comunes para tratar de mejorar la eficiencia del programador (ya no tener que preocuparse por estos errores). Hay menos errores (obvios y ocultos) con cadenas inmutables que con las mutables.
@Joachim: No estoy afirmando lo contrario.
Billy ONeal
1
Técnicamente, Common Lisp tiene cadenas mutables, para operaciones "similares a cadenas" y símbolos con nombres inmutables para identificadores inmutables.
Vatine
21

Razones que puedo recordar:

  1. La instalación del conjunto de cadenas sin hacer que la cadena sea inmutable no es posible en absoluto porque en el caso del grupo de cadenas, un objeto de cadena / literal, por ejemplo, "XYZ" será referenciado por muchas variables de referencia, por lo que si alguno de ellos cambia el valor, otros se verán afectados automáticamente. .

  2. La cadena se ha utilizado ampliamente como parámetro para muchas clases de Java, por ejemplo, para abrir una conexión de red, para abrir una conexión de base de datos, abrir archivos. Si String no es inmutable, esto conduciría a una seria amenaza de seguridad.

  3. La inmutabilidad permite a String almacenar en caché su código hash.

  4. Lo hace a prueba de hilos.

BOBO
fuente
7

1) Grupo de cadenas

El diseñador Java sabe que String será el tipo de datos más utilizado en todo tipo de aplicaciones Java y por eso querían optimizar desde el principio. Uno de los pasos clave en esa dirección fue la idea de almacenar literales de cadena en el grupo de cadenas. El objetivo era reducir el objeto String temporal compartiéndolos y para poder compartirlos, deben ser de la clase Inmutable. No puede compartir un objeto mutable con dos partes que son desconocidas entre sí. Tomemos un ejemplo hipotético, donde dos variables de referencia apuntan al mismo objeto String:

String s1 = "Java";
String s2 = "Java";

Ahora, si s1 cambia el objeto de "Java" a "C ++", la variable de referencia también obtuvo el valor s2 = "C ++", que ni siquiera sabe al respecto. Al hacer que String sea inmutable, este intercambio de String literal fue posible. En resumen, la idea clave del conjunto de cadenas no se puede implementar sin hacer que la secuencia sea definitiva o inmutable en Java.

2) seguridad

Java tiene un objetivo claro en términos de proporcionar un entorno seguro en todos los niveles de servicio y String es fundamental en todas esas cosas de seguridad. String se ha utilizado ampliamente como parámetro para muchas clases de Java, por ejemplo, para abrir la conexión de red, puede pasar el host y el puerto como String, para leer archivos en Java puede pasar la ruta de los archivos y el directorio como String y para abrir la conexión de la base de datos, puede pase la URL de la base de datos como String. Si String no era inmutable, un usuario podría haber otorgado acceso a un archivo en particular en el sistema, pero después de la autenticación puede cambiar la RUTA a otra cosa, esto podría causar serios problemas de seguridad. Del mismo modo, mientras se conecta a la base de datos o cualquier otra máquina en la red, el valor de String mutante puede presentar amenazas de seguridad. Las cadenas mutables también podrían causar problemas de seguridad en Reflection,

3) Uso de la cadena en el mecanismo de carga de clase

Otra razón para hacer que String sea final o inmutable fue el hecho de que se usó mucho en el mecanismo de carga de clase. Como String no ha sido inmutable, un atacante puede aprovechar este hecho y una solicitud para cargar clases estándar de Java, por ejemplo, java.io.Reader, puede cambiarse a una clase maliciosa com.unknown.DataStolenReader. Al mantener String final e inmutable, al menos podemos estar seguros de que JVM está cargando las clases correctas.

4) Beneficios de subprocesos múltiples

Dado que la concurrencia y el subprocesamiento múltiple era la oferta clave de Java, tenía mucho sentido pensar en la seguridad de subprocesos de los objetos String. Como se esperaba que String se usara ampliamente, lo que lo hace inmutable significa que no hay sincronización externa, significa un código mucho más limpio que implica compartir String entre múltiples subprocesos. Esta característica única hace que la codificación de concurrencia complicada, confusa y propensa a errores sea mucho más fácil. Como String es inmutable y solo lo compartimos entre subprocesos, da como resultado un código más legible.

5) Optimización y rendimiento

Ahora, cuando haces una clase Inmutable, sabes de antemano que esta clase no cambiará una vez creada. Esto garantiza una ruta abierta para muchas optimizaciones de rendimiento, por ejemplo, almacenamiento en caché. String sabe que no voy a cambiar, por lo que String almacena en caché su código hash. Incluso calcula el código hash perezosamente y, una vez creado, solo lo almacena en caché. En un mundo simple, cuando llama por primera vez al método hashCode () de cualquier objeto String, calcula el código hash y todas las llamadas posteriores a hashCode () devuelven el valor en caché ya calculado. Esto da como resultado una buena ganancia de rendimiento, dado que String se usa mucho en mapas basados ​​en hash, por ejemplo, Hashtable y HashMap. El almacenamiento en caché de hashcode no fue posible sin hacerlo inmutable y final, ya que depende del contenido de String.

saidesh kilaru
fuente
5

La máquina virtual Java realiza varias optimizaciones con respecto a las operaciones de cadena que no podrían realizarse de otra manera. Por ejemplo, si tenía una cadena con el valor "Mississippi" y asignó "Mississippi" .substring (0, 4) a otra cadena, hasta donde usted sabe, se hizo una copia de los primeros cuatro caracteres para hacer "Miss" . Lo que no sabe es que ambos comparten la misma cadena original "Mississippi", siendo uno el propietario y el otro una referencia de esa cadena desde la posición 0 a la 4. (La referencia al propietario evita que el propietario sea recogido por el recolector de basura cuando el propietario sale del alcance)

Esto es trivial para una cadena tan pequeña como "Mississippi", pero con cadenas más grandes y operaciones múltiples, ¡no tener que copiar la cadena es un gran ahorro de tiempo! Si las cadenas fueran mutables, entonces no podría hacer esto, porque modificar el original afectaría también a las "copias" de la subcadena.

Además, como menciona Donal, la ventaja se vería enormemente afectada por su desventaja. Imagine que escribe un programa que depende de una biblioteca y usa una función que devuelve una cadena. ¿Cómo podría estar seguro de que ese valor se mantendrá constante? Para asegurarse de que no ocurra tal cosa, siempre tendrá que producir una copia.

¿Qué pasa si tienes dos hilos que comparten la misma cadena? No querrás leer una cadena que está siendo reescrita por otro hilo, ¿verdad? Por lo tanto, la cadena tendría que ser segura para subprocesos, que siendo la clase común que es, haría que cada programa Java sea mucho más lento. De lo contrario, tendría que hacer una copia para cada hilo que requiera esa cadena o tendría que poner el código usando esa cadena en un bloque de sincronización, los cuales solo ralentizan su programa.

Por todas estas razones, esa fue una de las primeras decisiones tomadas para Java con el fin de diferenciarse de C ++.

Neil
fuente
Teóricamente, puede hacer una gestión de búfer de múltiples capas que permita copiar en mutación si se comparte, pero es muy difícil hacer que el trabajo sea eficiente en un entorno de subprocesos múltiples.
Donal Fellows
@DonalFellows Supuse que, dado que la máquina virtual Java no está escrita en Java (obviamente), se gestiona internamente utilizando punteros compartidos o algo similar.
Neil
5

La razón de la inmutabilidad de la cadena proviene de la coherencia con otros tipos primitivos en el lenguaje. Si tiene un intvalor que contiene el 42, y le agrega el valor 1, no cambia el 42. Obtiene un nuevo valor, 43, que no tiene relación alguna con los valores iniciales. Mutar primitivas que no sean cadenas no tiene sentido conceptual; y, como tales, los programas que tratan las cadenas como inmutables a menudo son más fáciles de razonar y comprender.

Además, Java realmente proporciona cadenas mutables e inmutables, como puede ver con StringBuilder; realmente, solo el valor predeterminado es la cadena inmutable. Si desea pasar referencias a StringBuildertodas partes, puede hacerlo. Java usa tipos separados ( Stringy StringBuilder) para estos conceptos porque no tiene soporte para expresar mutabilidad o falta de ella en su sistema de tipos. En los lenguajes que admiten la inmutabilidad en sus sistemas de tipos (p. Ej., C ++ const), a menudo hay un solo tipo de cadena que sirve para ambos propósitos.

Sí, tener una cadena inmutable le permite a uno implementar algunas optimizaciones específicas para cadenas inmutables, como internarse, y permite pasar referencias de cadena sin sincronización entre hilos. Sin embargo, esto confunde el mecanismo con el objetivo previsto de un lenguaje con un sistema de tipos simple y consistente. Comparo esto con la forma en que todos piensan que la recolección de basura es incorrecta; la recolección de basura no es "recuperación de memoria no utilizada"; es "simular una computadora con memoria ilimitada" . Las optimizaciones de rendimiento discutidas son cosas que se hacen para lograr que el objetivo de las cadenas inmutables funcione bien en máquinas reales; no es la razón de que tales cadenas sean inmutables en primer lugar.

Billy ONeal
fuente
@ Billy-Oneal .. Con respecto a "Si tiene un int que contiene el valor 42 y le agrega el valor 1, no cambia el 42. Obtiene un nuevo valor, 43, que no tiene relación alguna con el inicio valores." ¿Estás seguro de eso?
Shamit Verma
@ Shamit: Sí, estoy seguro. Sumar 1 a 42 da como resultado 43. No hace que el número 42 signifique lo mismo que el número 43.
Billy ONeal
@Shamit: Del mismo modo, no puedes hacer algo así 43 = 6y esperar que el número 43 signifique lo mismo que el número 6.
Billy ONeal
int i = 42; i = i + 1; este código almacenará 42 en la memoria y luego cambiará los valores en la misma ubicación a 43. Entonces, de hecho, la variable "i" adquiere un nuevo valor de 43.
Shamit Verma
@Shamit: En ese caso, mutaste i, no 42. Considera string s = "Hello "; s += "World";. Mutaste el valor de la variable s. Pero las cadenas "Hello ", "World"y "Hello World"son inmutables.
Billy ONeal
4

La inmutabilidad significa que las constantes mantenidas por clases que no son de su propiedad no pueden modificarse. Las clases que no son de su propiedad incluyen aquellas que están en el núcleo de la implementación de Java, y las cadenas que no deben modificarse incluyen elementos como tokens de seguridad, direcciones de servicio, etc. Realmente no debería poder modificar esos tipos de cosas (y esto se aplica doblemente cuando se opera en modo de espacio aislado).

Si String no era inmutable, cada vez que lo recuperaste de algún contexto que no quería que el contenido de la cadena cambiara bajo sus pies, deberías tomar una copia "por si acaso". Eso se pone muy caro.

Compañeros de Donal
fuente
44
Este mismo argumento se aplica a cualquier tipo, no solo a String. Pero, por ejemplo, los Arrays son mutables, no obstante. Entonces, ¿por qué Strings son inmutables y Arrayno s Y si la inmutabilidad es tan importante, ¿por qué Java hace que sea tan difícil crear y trabajar con objetos inmutables?
Jörg W Mittag
1
@ JörgWMittag: supongo que es básicamente una cuestión de cuán radicales querían ser. Tener una cadena inmutable fue bastante radical, en los días de Java 1.0. Tener un marco de recopilación inmutable (principalmente o incluso exclusivamente) también podría haber sido demasiado radical para obtener un uso amplio del lenguaje.
Joachim Sauer
Hacer un marco eficaz de colecciones inmutables es bastante difícil de realizar, hablando como alguien que ha escrito tal cosa (pero no en Java). También desearía totalmente tener matrices inmutables; eso me habría ahorrado bastante trabajo.
Donal Fellows
@DonalFellows: pcollections tiene como objetivo hacer eso (sin embargo, nunca lo usé yo mismo).
Joachim Sauer
3
@ JörgWMittag: hay personas (comúnmente desde una perspectiva puramente funcional) que argumentan que todos los tipos deberían ser inmutables. Del mismo modo, creo que si suma todos los problemas con los que se trabaja trabajando con estado mutable en software paralelo y concurrente, podría estar de acuerdo en que trabajar con objetos inmutables es a menudo mucho más fácil que los mutables.
Steven Evers
2

Imagine un sistema en el que acepta algunos datos, verifica su corrección y luego los pasa (para almacenarlos en una base de datos, por ejemplo).

Suponiendo que los datos son a Stringy deben tener al menos 5 caracteres de longitud. Su método se parece a esto:

public void handle(String input) {
  if (input.length() < 5) {
    throw new IllegalArgumentException();
  }
  storeInDatabase(input);
}

Ahora podemos estar de acuerdo en que cuando storeInDatabasese llama aquí, inputse ajustará al requisito. Pero si Stringfueran mutables, la persona que llama podría alterar el inputobjeto (de otro hilo) justo después de que se haya verificado y antes de que se haya almacenado en la base de datos . Esto requeriría un buen momento y probablemente no iría bien cada vez, pero ocasionalmente, podría lograr que almacene valores no válidos en la base de datos.

Los tipos de datos inmutables son una solución muy simple para este (y muchos otros problemas relacionados): cada vez que verifica algún valor, puede depender del hecho de que la condición verificada sigue siendo cierta más adelante.

Joachim Sauer
fuente
Gracias por la explicación. ¿Qué pasa si llamo método de manejo como este; handle (nueva cadena (input + "naberlan")). Supongo que puedo almacenar valores no válidos en la base de datos como este.
yfklon
1
@blank: así, ya que el inputdel handlemétodo ya es demasiado largo (no importa lo que el original, input es decir), sería simplemente una excepción. Estás creando una nueva entrada antes de llamar al método. Eso no es un problema.
Joachim Sauer
0

En general, encontrará tipos de valores y tipos de referencia . Con un tipo de valor, no le importa el objeto que lo representa, le importa el valor. Si le doy un valor, espera que ese valor permanezca igual. No quieres que cambie de repente. El número 5 es un valor. No esperas que cambie a 6 de repente. La cadena "Hola" es un valor. No espera que cambie a "P *** off" de repente.

Con los tipos de referencia, le importa el objeto y espera que cambie. Por ejemplo, a menudo esperará que cambie una matriz. Si le doy una matriz y desea mantenerla exactamente como está, debe confiar en mí para que no la cambie o hacer una copia.

Con la clase de cadena de Java, los diseñadores tuvieron que tomar una decisión: ¿es mejor si las cadenas se comportan como un tipo de valor, o deberían comportarse como un tipo de referencia? En el caso de las cadenas Java, se tomó la decisión de que deberían ser tipos de valor, lo que significa que, dado que son objetos, deben ser objetos inmutables.

Se podría haber tomado la decisión opuesta, pero en mi opinión habría causado muchos dolores de cabeza. Como se dijo en otra parte, muchos idiomas tomaron la misma decisión y llegaron a la misma conclusión. Una excepción es C ++, que tiene una clase de cadena, y las cadenas pueden ser constantes o no constantes, pero en C ++, a diferencia de Java, los parámetros de los objetos se pueden pasar como valores y no como referencias.

gnasher729
fuente
0

Estoy realmente sorprendido de que nadie haya señalado esto.

Respuesta: No te beneficiaría significativamente, incluso si fuera mutable. No te beneficiaría tanto como eso causa problemas adicionales. Examinemos los dos casos más comunes de mutación:

Cambiar un caracter de una cadena

Dado que cada carácter en una cadena Java toma 2 o 4 bytes, pregúntese, ¿ganaría algo si pudiera mutar la copia existente?

En el caso de que esté reemplazando un carácter de 2 bytes por 4 bytes uno (o viceversa), debe desplazar la parte restante de la cadena por 2 bytes hacia la izquierda o hacia la derecha. Lo cual no es tan diferente de copiar toda la cadena desde el punto de vista computacional.

Este también es un comportamiento realmente irregular que generalmente no es deseado. Imagine a alguien probando una aplicación con texto en inglés, y cuando la aplicación se adopta a países extranjeros, como China, todo comienza a funcionar de manera extraña.

Agregar otra cadena (o carácter) a la existente

Si tiene dos cadenas arbitrarias, esas se ubican en dos ubicaciones de memoria distintas. Si desea cambiar el primero agregando el segundo, no puede simplemente solicitar memoria adicional al final de la primera cadena, ya que probablemente ya esté ocupada.

Debe copiar la cadena concatenada en una ubicación completamente nueva, que es exactamente lo mismo que si ambas cadenas fueran inmutables.

Si desea hacer anexos de manera eficiente, es posible que desee usar StringBuilder, que reserva una cantidad considerable de espacio al final de una cadena, solo para este propósito de un posible anexo futuro.

Rok Kralj
fuente
-2
  1. son caros y mantenerlos inmutables permite cosas como subcadenas que comparten la matriz de bytes de la cadena principal. (aumento de velocidad también ya que no es necesario hacer una nueva matriz de bytes y copiar)

  2. seguridad: no querría que se cambiara el nombre de su paquete o código de clase

    [eliminado 3 viejos miró StrrBuilder src - no comparte memoria con cadena (hasta que se modifique) Creo que estaba en 1.3 o 1.4]

  3. código hash de caché

  4. para cadenas mutables, use SB (generador o buffer según sea necesario)

tgkprog
fuente
2
1. Por supuesto, existe la pena de no poder destruir las partes más grandes de la cadena si esto sucede. La internación no es gratuita; aunque mejora el rendimiento de muchos programas del mundo real. 2. Podría haber fácilmente "string" e "ImmutableString" que podrían satisfacer ese requisito. 3. No estoy seguro de entender que ...
Billy ONeal
.3. debería haber sido cachear el código hash. Esto también podría hacerse con una cadena mutable. @ billy-oneal
tgkprog
-4

Las cadenas deberían haber sido un tipo de datos primitivo en Java. Si lo hubieran sido, las cadenas pasarían a ser mutables y la palabra clave final generaría cadenas inmutables. Las cadenas mutables son útiles y, por lo tanto, existen múltiples hacks para cadenas mutables en las clases stringbuffer, stringbuilder y charsequence.

CWallach
fuente
3
Esto no responde al aspecto "por qué" de lo que es ahora que la pregunta sí hace. Además, java final no funciona de esa manera. Las cadenas mutables no son hacks, sino más bien consideraciones de diseño reales basadas en los usos más comunes de las cadenas y las optimizaciones que se pueden hacer para mejorar la jvm.
1
La respuesta al "por qué" es una mala decisión de diseño del lenguaje. Tres formas ligeramente diferentes de admitir cadenas mutables es un hack que el compilador / JVM debería manejar.
CWallach
3
String y StringBuffer fueron los originales. StringBuilder fue agregado luego reconociendo una dificultad de diseño con StringBuffer. Las cadenas mutables e inmutables que son objetos diferentes se encuentran en muchos idiomas, ya que la consideración del diseño se hizo una y otra vez y decidió que cada uno de ellos es un objeto diferente cada vez. C # "Las cadenas son inmutables" y ¿Por qué la cadena .NET es inmutable? , el objetivo C NSString es inmutable mientras NSMutableString es mutable. stackoverflow.com/questions/9544182