Pregunta

Esta podría ser la pregunta más tonta que se haya hecho, pero creo que es bastante confuso para un novato en Java.

  1. ¿Alguien puede aclarar qué se entiende por inmutable ?
  2. ¿Por qué es inmutable una String ?
  3. ¿Cuáles son las ventajas / desventajas de los objetos inmutables?
  4. ¿Por qué debería preferirse un objeto mutable como StringBuilder sobre String y viceversa?

Un buen ejemplo (en Java) será realmente apreciado.

¿Fue útil?

Solución

Inmutable significa que una vez que el constructor de un objeto ha completado la ejecución, esa instancia no puede ser alterada.

Esto es útil ya que significa que puede pasar referencias al objeto alrededor, sin preocuparse de que alguien más vaya a cambiar su contenido. Especialmente cuando se trata de concurrencia, no hay problemas de bloqueo con objetos que nunca cambian

por ejemplo

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 imagina una clase similar a Foo , pero con un StringBuilder en lugar de un String como miembro, puede ver que un la persona que llama 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 del escenario estado privado de mutables real (y, por lo tanto, no se puede compartir de forma segura entre subprocesos).

Otros consejos

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 caracteres tiene que ser copiado! Si el objeto String fuera mutable (los caracteres podrían cambiar más tarde), entonces tendría que copiar todos los caracteres, de lo contrario cambia a caracteres en el la subcadena se reflejaría en la otra cadena más tarde.

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 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. Tome 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 algo más podría estar haciendo referencia a ellos, no puedes 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 la concatenación se llama dos veces , lo que significa que se debe recorrer toda la cadena. Compare esto con el código C para la operación 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 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();
}

Dado que el objeto mutable 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 tanto de mutabilidad como de inmutabilidad.

En realidad, la cadena no es inmutable si usa la definición de wikipedia sugerida anteriormente.

El estado de la cadena cambia la construcción posterior. Eche un vistazo al método hashcode (). La cadena almacena en caché el valor de hashcode en un campo local, pero no lo calcula hasta la primera llamada de hashcode (). Esta evaluación perezosa de hashcode 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 puede 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?

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 en un objeto.

Solo para aclarar, sin embargo, StringBuilder es en realidad un objeto mutable, no inmutable. Una cadena Java normal es inmutable (lo que significa que una vez que se ha creado no se puede cambiar la cadena subyacente sin cambiar el objeto).

Por ejemplo, supongamos que tengo una clase llamada ColoredString que tiene un valor de cadena y un color de cadena:

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 ColourStrings para imprimir datos en la ventana. Si tiene una instancia de ColouredString que se creó como

new ColoredString("Blue", "This is a blue string!");

Entonces esperarías que la cadena siempre sea " Azul " ;. Si otro hilo, sin embargo, se puso en contacto con esta instancia y llamó

blueString.setColor("Red");

De repente, y probablemente de forma inesperada, ahora tendría un "Rojo" cadena cuando quería un " Azul " uno. Debido a esto, los objetos inmutables son casi siempre preferidos al pasar instancias de objetos alrededor. Cuando tiene un caso en el que los objetos mutables son realmente necesarios, normalmente protegerá el objeto pasando solo copias de su campo de control específico.

Para recapitular, en Java, java.lang.String es un objeto inmutable ( no puede cambiarse una vez creado) y java.lang.StringBuilder es un objeto mutable porque puede cambiarse sin creando una nueva instancia.

  1. En aplicaciones grandes, es común que los literales de cadena ocupen grandes bits de memoria. Entonces, para manejar eficientemente la memoria, la JVM asigna un área llamada " Conjunto constante de cadenas ". ( Tenga en cuenta que en la memoria, incluso una cadena sin referencia lleva un char [], un int para su longitud y otro para su hashCode. Para un número, por el contrario, un máximo de ocho bytes inmediatos es requerido )
  2. Cuando el compilador encuentra un literal de cadena, comprueba el grupo 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).
  3. Por lo tanto: La mutabilidad de cadena ahorra memoria ...
  4. Pero cuando cualquiera de las variables cambia el valor, en realidad, solo cambia su referencia, no el valor en la memoria (por lo tanto, no afectará a las otras variables que lo hacen referencia) 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 el   La variable de referencia se cambió para que se refiera a la nueva cadena.   Y si no tuviéramos s2, "Old String" todavía estaría en la memoria pero   no podremos acceder a él ...

" inmutable " significa que no puedes cambiar el valor. Si tiene una instancia de la clase String, cualquier método que llame que parezca modificar el valor, en realidad creará otra String.

String foo = "Hello";
foo.substring(3);
<-- foo here still has the same value "Hello"

Para preservar 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 sucederá si usa un objeto mutable como clave para el mapa y luego cambie el valor (consejo: piense en equals y hashCode ).

java.time

Puede ser 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 , así que en el siguiente ejemplo

LocalDate date = LocalDate.of(2014, 3, 18); 
date.plusYears(2);
System.out.println(date);

Salida:

  

2014-03-18

Esto imprime el mismo año que la fecha inicial porque plusYears (2) devuelve un nuevo objeto, por lo que la fecha anterior aún no cambia porque es un objeto inmutable. Una vez creado, no puede modificarlo más y la variable de fecha todavía lo señala.

Entonces, ese ejemplo de código debería capturar y usar el nuevo objeto instanciado y devuelto por esa llamada a plusYears .

LocalDate date = LocalDate.of(2014, 3, 18); 
LocalDate dateAfterTwoYears = date.plusYears(2);
  

date.toString () & # 8230; 2014-03-18

     

dateAfterTwoYears.toString () & # 8230; 2016-03-18

Realmente me gusta la explicación de Guía de estudio SCJP Sun Certified Programmer para Java 5 .

  

Para que Java sea más eficiente en cuanto a memoria, la JVM reserva un área especial de memoria llamada el "conjunto constante de cadenas". Cuando el compilador encuentra un literal de cadena, comprueba el grupo 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 un nuevo objeto literal de Cadena.

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, todas las cuales ayudarán a reducir la cantidad de errores que introduce en su código:

  • Es mucho más fácil razonar sobre cómo funciona su programa cuando sabe que el estado de un objeto no se puede cambiar por otro método
  • Los objetos inmutables son seguros para subprocesos automáticamente (suponiendo que se publiquen de forma segura), por lo que nunca serán la causa de esos errores de subprocesamiento múltiple difíciles de identificar
  • Los objetos inmutables siempre tendrán el mismo código Hash, por lo que pueden usarse como claves en un HashMap (o similar). Si el código hash de un elemento en una tabla hash cambiara, la entrada de la tabla se perdería efectivamente, ya que los intentos de encontrarlo en la tabla terminarían buscando en el lugar equivocado. Esta es la razón principal por la que los objetos String son inmutables: se usan con frecuencia como claves HashMap.

También hay algunas otras optimizaciones que puede hacer en el código cuando sabe que el estado de un objeto es inmutable, por ejemplo, el almacenamiento en caché calculado, pero estas son optimizaciones y, por lo tanto, no son tan interesantes.

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. 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.

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 = " Hola " : se creó un objeto s1 con " Hola " valor en ella.

s2 = s1 : se crea un objeto s2 con referencia al objeto s1.

s1 = " Bye " : el valor del objeto s1 anterior no cambia porque s1 tiene el tipo String y el tipo String es un tipo inmutable, en su lugar, el compilador crea un nuevo objeto String con " Bye " valor y s1 referenciado a él. aquí cuando imprimimos el valor s2 , el resultado será "Hola" no '' Adiós '' porque s2 hace referencia al objeto s1 anterior que tenía '' Hola '' valor.

Inmutable significa que una vez que se crea el objeto, ninguno de sus miembros cambiará. String es inmutable ya que no puede cambiar su contenido. Por ejemplo:

String s1 = "  abc  ";
String s2 = s1.trim();

En el código anterior, la cadena s1 no cambió, se creó otro objeto ( s2 ) usando s1 .

Inmutable simplemente significa inmutable o inmodificable. Una vez que se crea el 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,

 ingrese la descripción de la imagen aquí

En este diagrama, puede ver un nuevo objeto creado como " Future World " ;. Pero no cambie "Futuro". Porque String es inmutable . s , todavía se refieren a " Futuro " ;. Si necesita llamar a "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á afectado por todas las variables de referencia. Es por eso que los objetos de cadena son inmutables en Java.

Una vez instanciado, no puede ser alterado. Considere una clase de la que una instancia podría usarse como clave para una tabla hash o similar. Consulte las mejores prácticas de Java.

Objetos inmutables

Un objeto se considera inmutable si su estado no puede cambiar después de su construcción. La confianza máxima 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. Como no pueden cambiar de estado, no pueden corromperse por interferencia de hilos ni observarse en un estado inconsistente.

Los programadores a menudo son reacios a emplear objetos inmutables, porque les preocupa 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 disminución de la sobrecarga debido a la recolección de basura y la eliminación del código necesario para proteger los objetos mutables de la corrupción.

Las siguientes subsecciones toman una clase cuyas instancias son mutables y derivan de ella una clase con instancias inmutables. Al hacerlo, dan reglas generales para este tipo de conversión y demuestran algunas de las ventajas de los objetos inmutables.

Fuente

oracle docs dice

  

Un objeto se considera inmutable si su estado no puede cambiar después de él   esta construido. La confianza máxima en objetos inmutables es ampliamente   aceptado como una buena estrategia para crear código simple y confiable.

     

Los objetos inmutables son particularmente útiles en aplicaciones concurrentes.   Como no pueden cambiar de estado, no pueden ser corrompidos por el hilo   interferencia u observado en un estado inconsistente.

Me gusta esta frase de la publicación

  

Los objetos inmutables facilitan la programación de concurrencia

Un objeto inmutable es el que no puede modificar después de crearlo. Un ejemplo típico son los literales de cadena.

Un lenguaje de programación D, que se vuelve cada vez más popular, tiene la noción de "inmutabilidad". a través de "invariante" palabra clave. Consulte este artículo del Dr.Dobb al respecto: http: // dobbscodetalk .com / index.php? option = com_myblog & amp; show = Invariant-Strings.html & amp; Itemid = 29 . Explica el problema perfectamente.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top