java - valores - ¿Qué se entiende por inmutable?
objetos mutables e inmutables java (18)
Esta podría ser la pregunta más tonta que se haya hecho, pero creo que es una confusión total para un novato.
- ¿Alguien puede aclarar qué se entiende por inmutable ?
- ¿Por qué es inmutable una
String
? - ¿Cuáles son las ventajas / desventajas de los objetos inmutables?
- ¿Por qué debería preferirse un objeto mutable como
StringBuilder
a String y vice-verse?
Un buen ejemplo (en Java) será realmente apreciado.
java.time
Puede que sea un poco tarde, pero para comprender qué es un objeto inmutable, considere el siguiente ejemplo de la nueva API de fecha y hora de Java 8 ( java.time ). Como probablemente sepa, todos los objetos de fecha de Java 8 son inmutables, por lo que en el siguiente ejemplo
LocalDate date = LocalDate.of(2014, 3, 18);
date.plusYears(2);
System.out.println(date);
Salida:
2014-03-18
Esto se imprime el mismo año que la fecha inicial porque el plusYears(2)
devuelve un nuevo objeto, por lo que la fecha anterior aún no se modifica porque es un objeto inmutable. Una vez creado, no puede seguir modificándolo y la variable de fecha aún lo apunta.
Entonces, ese ejemplo de código debería capturar y usar el nuevo objeto creado y devuelto por esa llamada a plusYears
.
LocalDate date = LocalDate.of(2014, 3, 18);
LocalDate dateAfterTwoYears = date.plusYears(2);
date.toString ()… 2014-03-18
dateAfterTwoYears.toString ()… 2016-03-18
- En aplicaciones grandes, es común que los literales de cadena ocupen grandes bits de memoria. Por lo tanto, para manejar la memoria de manera eficiente, la JVM asigna un área llamada "grupo de constantes de cadenas". ( Tenga en cuenta que incluso en una cadena sin referencia se incluye un carácter char [], un int para su longitud y otro para su código hash. Para un número , por el contrario, se requiere un máximo de ocho bytes inmediatos )
- Cuando complier se encuentra con un literal de cadena, verifica la agrupación para ver si ya existe un literal idéntico. Y si se encuentra uno, la referencia al nuevo literal se dirige a la cadena existente, y no se crea un nuevo ''objeto literal de cadena'' (la cadena existente simplemente obtiene una referencia adicional).
- Por lo tanto: la mutabilidad de cadenas ahorra memoria ...
- Pero cuando cualquiera de las variables cambia de valor, en realidad, solo se modifica su referencia, no el valor en la memoria (por lo tanto, no afectará a las otras variables que hacen referencia a ella) como se ve a continuación ...
Cadena s1 = "Cadena antigua";
//s1 variable, refers to string in memory
reference | MEMORY |
variables | |
[s1] --------------->| "Old String" |
Cadena s2 = s1;
//s2 refers to same string as s1
| |
[s1] --------------->| "Old String" |
[s2] ------------------------^
s1 = "Nueva cadena";
//s1 deletes reference to old string and points to the newly created one
[s1] -----|--------->| "New String" |
| | |
|~~~~~~~~~X| "Old String" |
[s2] ------------------------^
La cadena original ''en memoria'' no cambió, pero la variable de referencia se cambió para que se refiera a la nueva cadena. Y si no tuviéramos s2, "Cadena antigua" aún estaría en la memoria pero no podremos acceder a ella ...
"inmutable" significa que no puede cambiar el valor. Si tiene una instancia de clase String, cualquier método al que llame que parezca modificar el valor, creará otra String.
String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"
Para conservar los cambios, debe hacer algo como esto foo = foo.sustring (3);
Inmutable vs mutable puede ser divertido cuando trabajas con colecciones. Piense en lo que ocurrirá si usa un objeto mutable como clave para el mapa y luego cambie el valor (consejo: piense en equals
y hashCode
).
En realidad, String no es inmutable si usas la definición de wikipedia sugerida anteriormente.
El estado de String cambia después de la construcción. Echa un vistazo al método hashcode (). La cadena almacena en caché el valor del código hash en un campo local, pero no lo calcula hasta la primera llamada de código hash (). Esta perezosa evaluación del código de hash coloca a String en una posición interesante como un objeto inmutable cuyo estado cambia, pero no se puede observar que haya cambiado sin usar la reflexión.
Entonces, tal vez la definición de inmutable debería ser un objeto que no se pueda observar que haya cambiado.
Si el estado cambia en un objeto inmutable después de haber sido creado pero nadie puede verlo (sin reflexión), ¿el objeto sigue siendo inmutable?
Inmutable significa que una vez que el constructor de un objeto ha finalizado la ejecución, esa instancia no puede modificarse.
Esto es útil porque significa que puede pasar referencias al objeto, sin preocuparse de que alguien más va a cambiar su contenido. Especialmente cuando se trata de la concurrencia, no hay problemas de bloqueo con objetos que nunca cambian
p.ej
class Foo
{
private final String myvar;
public Foo(final String initialValue)
{
this.myvar = initialValue;
}
public String getValue()
{
return this.myvar;
}
}
Foo
no tiene que preocuparse de que la persona que llama a getValue()
pueda cambiar el texto en la cadena.
Si imaginas una clase similar a Foo
, pero con un StringBuilder
lugar de una String
como miembro, puedes ver que un llamador a getValue()
podría alterar el atributo StringBuilder
de una instancia de Foo
.
También tenga cuidado con los diferentes tipos de inmutabilidad que puede encontrar: Eric Lippert escribió un artículo de blog sobre esto. Básicamente, puede tener objetos cuya interfaz es inmutable, pero detrás de la escena se puede intercambiar el estado privado (y, por lo tanto, no se puede compartir de forma segura entre los hilos).
Inmutable significa que una vez que se crea el objeto, ninguno de sus miembros cambiará. String
es inmutable ya que no puedes cambiar su contenido. Por ejemplo:
String s1 = " abc ";
String s2 = s1.trim();
En el código anterior, la cadena s1 no cambió, otro objeto ( s2
) se creó utilizando s1
.
Inmutable significa simplemente inmutable o no modificable. Una vez que se crea un objeto de cadena, sus datos o estado no se pueden cambiar
Considere el siguiente ejemplo,
class Testimmutablestring{
public static void main(String args[]){
String s="Future";
s.concat(" World");//concat() method appends the string at the end
System.out.println(s);//will print Future because strings are immutable objects
}
}
Vamos a tener una idea considerando el siguiente diagrama,
En este diagrama, puedes ver un nuevo objeto creado como "Mundo Futuro". Pero no cambia el "futuro". Because String is immutable
. s
, todavía se refieren a "futuro". Si necesitas llamar "Future World",
String s="Future";
s=s.concat(" World");
System.out.println(s);//print Future World
¿Por qué los objetos de cadena son inmutables en java?
Porque Java usa el concepto de cadena literal. Supongamos que hay 5 variables de referencia, todas se refieren a un objeto "Futuro". Si una variable de referencia cambia el valor del objeto, se verá afectada a todas las variables de referencia. Es por eso que los objetos de cadena son inmutables en java.
Los objetos inmutables son objetos que no se pueden cambiar mediante programación. Son especialmente buenos para entornos de subprocesos múltiples u otros entornos donde más de un proceso puede alterar (mutar) los valores de un objeto.
Sin embargo, solo para aclarar, StringBuilder es en realidad un objeto mutable, no uno inmutable. Una cadena java normal es inmutable (lo que significa que una vez creada, no puede cambiar la cadena subyacente sin cambiar el objeto).
Por ejemplo, digamos que tengo una clase llamada ColoredString que tiene un valor de String y un color de String:
public class ColoredString {
private String color;
private String string;
public ColoredString(String color, String string) {
this.color = color;
this.string = string;
}
public String getColor() { return this.color; }
public String getString() { return this.string; }
public void setColor(String newColor) {
this.color = newColor;
}
}
En este ejemplo, se dice que ColoredString es mutable porque puede cambiar (mutar) una de sus propiedades clave sin crear una nueva clase ColoredString. La razón por la que esto puede ser malo es, por ejemplo, supongamos que tiene una aplicación GUI que tiene varios subprocesos y está utilizando ColoredStrings para imprimir datos en la ventana. Si tiene una instancia de ColoredString que fue creada como
new ColoredString("Blue", "This is a blue string!");
Entonces esperarías que la cadena siempre fuera "Azul". Si otro hilo, sin embargo, consiguió una copia de esta instancia y llamó
blueString.setColor("Red");
De repente, y probablemente de forma inesperada, ahora tendrías una cadena "Roja" cuando quisieras una "Azul". Debido a esto, los objetos inmutables casi siempre se prefieren cuando se pasan instancias de objetos. Cuando tiene un caso en el que los objetos mutables son realmente necesarios, entonces normalmente protegería el objeto al pasar solo copias de su campo de control específico.
Para resumir, en Java, java.lang.String es un objeto inmutable (no se puede cambiar una vez que se crea) y java.lang.StringBuilder es un objeto mutable porque se puede cambiar sin crear una nueva instancia.
Los objetos que son inmutables no pueden cambiar su estado después de haber sido creados.
Hay tres razones principales para usar objetos inmutables siempre que pueda, todo lo cual ayudará a reducir la cantidad de errores que introduce en su código:
- Es mucho más fácil razonar acerca de cómo funciona su programa cuando sabe que el estado de un objeto no se puede cambiar con otro método.
- Los objetos inmutables son seguros para subprocesos automáticamente (asumiendo que se publican de manera segura) por lo que nunca será la causa de esos errores de subprocesos múltiples de difícil acceso hacia abajo
- Los objetos inmutables siempre tendrán el mismo código Hash, por lo que se pueden usar como claves en un HashMap (o similar). Si se cambiara el código hash de un elemento en una tabla hash, la entrada de la tabla se perdería efectivamente, ya que los intentos de encontrarlo en la tabla terminarán buscando en el lugar equivocado. Esta es la razón principal por la que los objetos String son inmutables: se utilizan con frecuencia como claves HashMap.
También puede hacer algunas otras optimizaciones en el código cuando sabe que el estado de un objeto es inmutable, por ejemplo, almacenar en caché el hash calculado, pero estas son optimizaciones y, por lo tanto, no son tan interesantes.
Me gusta mucho la explicación de SCJP Sun Certified Programmer para Java 5 Guía de estudio .
Para hacer que Java sea más eficiente en la memoria, la JVM pone a un lado un área especial de memoria llamada "grupo de constantes de cadenas". Cuando el compilador encuentra un literal de cadena, verifica la agrupación para ver si ya existe una cadena idéntica. Si se encuentra una coincidencia, la referencia al nuevo literal se dirige a la cadena existente, y no se crea ningún nuevo objeto de cadena literal.
Un objeto inmutable es aquel que no puede modificar después de crearlo. Un ejemplo típico son los literales de cuerdas.
El lenguaje de programación AD, que se vuelve cada vez más popular, tiene una noción de "inmutabilidad" a través de una palabra clave "invariante". Consulte este artículo de Dr.Dobb al respecto: http://dobbscodetalk.com/index.php?option=com_myblog&show=Invariant-Strings.html&Itemid=29 . Explica perfectamente el problema.
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 cuerdas inmutables tienen muchas ventajas:
Rendimiento: tomar 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 caracteres tiene que ser copiado! Si el objeto String fuera mutable (los caracteres podrían cambiar más adelante), entonces tendría que copiar todos los caracteres, de lo contrario los cambios en los caracteres en la subcadena se reflejarán 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 Thread Safe .
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 desventajas a la inmutabilidad:
Rendimiento: espera, ¡pensé que habías dicho 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". No solo la segunda pieza de código es más legible, sino que es más rápida. 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 refiriéndose a foo, no puedes simplemente cambiarlo; tiene que copiar la cadena completa (por supuesto, parte de esta funcionalidad se abstrae en funciones en la C real subyacente, 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 a la concatenación se le llama dos veces, lo que significa que toda la cadena debe estar conectada! Compare esto con el código C para la operación de la bar
:
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 muchas cosas e insertarse en una cadena, necesita la mutabilidad para la velocidad. Si desea los beneficios de la seguridad de concurrencia y la recolección de basura, la clave es mantener sus objetos mutables en un método local:
// 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();
}
Dado que el objeto mutable
es una referencia local, no tiene que preocuparse por la seguridad de concurrencia (solo un hilo lo toca). Y como 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 obtiene todos los beneficios de rendimiento tanto de mutabilidad como de inmutabilidad.
Un significado tiene que ver con cómo se almacena el valor en la computadora. Para una cadena .Net, por ejemplo, significa que la cadena en la memoria no se puede cambiar. Cuando crees que la estás cambiando, estás creando una nueva cadena en la memoria y apuntando la variable existente (que es solo un puntero a la colección real de caracteres en otro lugar) a la nueva cadena.
Una vez instanciado, no puede ser alterado. Considere una clase que una instancia de podría usarse como la clave para una tabla hash o similar. Echa un vistazo a las mejores prácticas de Java.
Un objeto se considera inmutable si su estado no puede cambiar después de que se construye. La máxima confianza en objetos inmutables es ampliamente aceptada como una estrategia sólida para crear código simple y confiable.
Los objetos inmutables son particularmente útiles en aplicaciones concurrentes. Ya que no pueden cambiar de estado, no pueden ser corrompidos por la interferencia del hilo u observados en un estado inconsistente.
Me gusta esta frase del post
Objetos inmutables facilitan la programación de concurrencia.
¿Qué es un objeto inmutable?
Según la documentación de referencia de Java, "Un objeto se considera inmutable si su estado no puede cambiar después de que se construye" Simplemente una clase inmutable es una clase cuyas propiedades no se pueden modificar después de la creación. Eso significa que en la creación, todos los datos de la instancia se proporcionan y permanecen sin cambios hasta la destrucción de los objetos.
¿Por qué objetos inmutables?
Las clases inmutables son ideales para aplicaciones concurrentes. De acuerdo con su naturaleza inmutable, ayudan a mantener la aplicación en un comportamiento coherente y no corrupto, ya que el estado de las instancias no se puede cambiar.
Preocupaciones
Las clases inmutables son fáciles de diseñar, desarrollar y usar, ya que son más resistentes a los errores y seguras debido a la inmutabilidad. Sin embargo, los programadores son reacios a usarlos debido a la limitación de reutilización. También puede tener un impacto en el rendimiento de la creación de objetos que la reutilización. Sin embargo, con los últimos compiladores sofisticados, el costo de creación es mínimo. También las capacidades provistas y la ventaja de la inmutabilidad pueden ser adoptadas más allá de la reutilización según la necesidad.
¿Cómo funciona este anexo String está funcionando?
El nuevo objeto String se crea con el valor dado y la referencia se actualiza a la nueva instancia que aísla el objeto anterior.
Referencia: http://www.devdummy.com/2017/09/immutable-objects-in-java.html
Objetos inmutables
Un objeto se considera inmutable si su estado no puede cambiar después de que se construye. La máxima confianza en objetos inmutables es ampliamente aceptada como una estrategia sólida para crear código simple y confiable.
Los objetos inmutables son particularmente útiles en aplicaciones concurrentes. Ya que no pueden cambiar de estado, no pueden ser corrompidos por la interferencia del hilo u observados en un estado inconsistente.
Los programadores a menudo son reacios a emplear objetos inmutables, porque se preocupan por el costo de crear un nuevo objeto en lugar de actualizar un objeto en su lugar. El impacto de la creación de objetos a menudo se sobreestima y puede compensarse con algunas de las eficiencias asociadas con los objetos inmutables. Estos incluyen una reducción de los gastos generales debido a la recolección de basura, y la eliminación del código necesario para proteger los objetos mutables contra la corrupción.
Las siguientes subsecciones toman una clase cuyas instancias son mutables y derivan una clase con instancias inmutables de ella. Al hacerlo, dan reglas generales para este tipo de conversión y demuestran algunas de las ventajas de los objetos inmutables.
String s1="Hi";
String s2=s1;
s1="Bye";
System.out.println(s2); //Hi (if String was mutable output would be: Bye)
System.out.println(s1); //Bye
s1="Hi"
: se creó un objeto s1
con el valor "Hola".
s2=s1
: un objeto s2
se crea con referencia al objeto s1.
s1="Bye"
: el valor del objeto s1
anterior no cambia porque s1
tiene el tipo String y String type es un tipo inmutable, en su lugar, el compilador crea un nuevo objeto String con el valor "Bye" y s1
referencia a él. aquí, cuando imprimimos el valor de s2
, el resultado será "Hola" no "Bye" porque s2
referencia al objeto s1
anterior que tenía el valor "Hola".