Un objeto inmutable es un objeto donde los campos internos (o al menos, todos los campos internos que afectan su comportamiento externo) no se pueden cambiar.
Las cadenas inmutables tienen muchas ventajas:
Rendimiento: realice la siguiente operación:
String substring = fullstring.substring(x,y);
La C subyacente para el método substring () es probablemente algo como esto:
// Assume string is stored like this:
struct String { char* characters; unsigned int length; };
// Passing pointers because Java is pass-by-reference
struct String* substring(struct String* in, unsigned int begin, unsigned int end)
{
struct String* out = malloc(sizeof(struct String));
out->characters = in->characters + begin;
out->length = end - begin;
return out;
}
¡Tenga en cuenta que ninguno de los personajes tiene que ser copiado! Si el objeto String fuera mutable (los caracteres podrían cambiar más tarde), tendría que copiar todos los caracteres; de lo contrario, los cambios en los caracteres de la subcadena se reflejarían en la otra cadena más adelante.
Concurrencia: si la estructura interna de un objeto inmutable es válida, siempre será válida. No hay posibilidad de que diferentes hilos puedan crear un estado no válido dentro de ese objeto. Por lo tanto, los objetos inmutables son seguros para subprocesos .
Recolección de basura: es mucho más fácil para el recolector de basura tomar decisiones lógicas sobre objetos inmutables.
Sin embargo, también hay inconvenientes en la inmutabilidad:
Rendimiento: ¡ Espera, pensé que dijiste que el rendimiento era una ventaja de la inmutabilidad! Bueno, a veces lo es, pero no siempre. Toma el siguiente código:
foo = foo.substring(0,4) + "a" + foo.substring(5); // foo is a String
bar.replace(4,5,"a"); // bar is a StringBuilder
Las dos líneas reemplazan el cuarto carácter con la letra "a". El segundo código no solo es más legible, sino que es más rápido. Mira cómo tendrías que hacer el código subyacente para foo. Las subcadenas son fáciles, pero ahora porque ya hay un personaje en el espacio cinco y otra cosa podría estar haciendo referencia a foo, no puedes simplemente cambiarlo; debe copiar toda la cadena (por supuesto, parte de esta funcionalidad se abstrae en funciones en el C subyacente real, pero el punto aquí es mostrar el código que se ejecuta en un solo lugar).
struct String* concatenate(struct String* first, struct String* second)
{
struct String* new = malloc(sizeof(struct String));
new->length = first->length + second->length;
new->characters = malloc(new->length);
int i;
for(i = 0; i < first->length; i++)
new->characters[i] = first->characters[i];
for(; i - first->length < second->length; i++)
new->characters[i] = second->characters[i - first->length];
return new;
}
// The code that executes
struct String* astring;
char a = 'a';
astring->characters = &a;
astring->length = 1;
foo = concatenate(concatenate(slice(foo,0,4),astring),slice(foo,5,foo->length));
Tenga en cuenta que concatenate se llama dos veces, lo que significa que se debe recorrer toda la cadena. Compare esto con el código C para la bar
operación:
bar->characters[4] = 'a';
La operación de cadena mutable es obviamente mucho más rápida.
En conclusión: en la mayoría de los casos, desea una cadena inmutable. Pero si necesita agregar e insertar mucho en una cadena, necesita la mutabilidad para la velocidad. Si desea los beneficios de concurrencia de seguridad y recolección de basura, la clave es mantener sus objetos mutables locales a un método:
// This will have awful performance if you don't use mutable strings
String join(String[] strings, String separator)
{
StringBuilder mutable;
boolean first = true;
for(int i = 0; i < strings.length; i++)
{
if(!first) first = false;
else mutable.append(separator);
mutable.append(strings[i]);
}
return mutable.toString();
}
Como el mutable
objeto es una referencia local, no tiene que preocuparse por la seguridad de concurrencia (solo un hilo lo toca). Y dado que no se hace referencia en ningún otro lugar, solo se asigna en la pila, por lo que se desasigna tan pronto como finaliza la llamada a la función (no tiene que preocuparse por la recolección de basura). Y obtienes todos los beneficios de rendimiento de mutabilidad e inmutabilidad.