Colección inmutable vs inmodificable

170

Desde el Resumen del marco de colecciones :

Las colecciones que no admiten operaciones de modificación (como add, removey clear) se denominan no modificables . Las colecciones que no son inmodificables son modificables .

Las colecciones que además garantizan que ningún cambio en el Collectionobjeto será visible se denominan inmutables . Las colecciones que no son inmutables son mutables .

No puedo entender la distinción.
¿Cuál es la diferencia entre inmodificable e inmutable aquí?

Cratilo
fuente

Respuestas:

207

Una colección no modificable a menudo es un contenedor alrededor de una colección modificable a la que otros códigos aún pueden tener acceso . Entonces, si bien no puede realizar ningún cambio si solo tiene una referencia a la colección no modificable, no puede confiar en que el contenido no cambie.

Una colección inmutable garantiza que ya nada puede cambiar la colección. Si envuelve una colección modificable, se asegura de que ningún otro código tenga acceso a esa colección modificable. Tenga en cuenta que, aunque ningún código puede cambiar a qué objetos contiene referencias la colección, los objetos en sí mismos pueden ser mutables; crear una colección inmutable de StringBuilderalguna manera no "congela" esos objetos.

Básicamente, la diferencia está en si otro código puede cambiar la colección a sus espaldas.

Jon Skeet
fuente
63
Una colección inmutable no garantiza que nada pueda cambiar más. Solo se asegura de que la colección en sí no se pueda alterar (y no mediante el ajuste, sino mediante la copia). Los objetos que están presentes en la colección aún pueden alterarse, y no se les garantiza.
Hiery Nomus
8
@HieryNomus: Tenga en cuenta que no dije que nada podría cambiar, dije que nada podría cambiar la colección.
Jon Skeet
1
ok, podría haber leído mal eso;) Pero es bueno aclararlo.
Hiery Nomus
55
Entonces, lo que estás diciendo. Para una verdadera inmutabilidad, necesita una colección inmutable que contenga elementos de un tipo inmutable.
Evan Plaice
1
@savanibharat: eso depende de si hay alguna ruta de código que aún pueda modificarse list. Si algo puede llamar list.add(10)luego coll, reflejará ese cambio, así que no, no lo llamaría inmutable.
Jon Skeet
92

Básicamente, unModifiableCollection es una vista, por lo que, indirectamente, aún podría 'modificarse' desde alguna otra referencia que sea modificable. Además, como es solo una vista de solo lectura de otra colección, cuando la colección de origen cambia, la Colección no modificable siempre se presentará con los últimos valores.

Sin embargo, la immutableColección puede tratarse como una copia de solo lectura de otra colección y no puede modificarse. En este caso, cuando la colección de origen cambia, la Colección inmutable no refleja los cambios.

Aquí hay un caso de prueba para visualizar esta diferencia.

@Test
public void testList() {

    List<String> modifiableList = new ArrayList<String>();
    modifiableList.add("a");

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("--");


    //unModifiableList

    assertEquals(1, modifiableList.size());

    List<String> unModifiableList=Collections.unmodifiableList(
                                        modifiableList);

    modifiableList.add("b");

    boolean exceptionThrown=false;
    try {
        unModifiableList.add("b");
        fail("add supported for unModifiableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("unModifiableList.add() not supported");
    }
    assertTrue(exceptionThrown);

    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);

    assertEquals(2, modifiableList.size());
    assertEquals(2, unModifiableList.size());
            System.out.println("--");



            //immutableList


    List<String> immutableList=Collections.unmodifiableList(
                            new ArrayList<String>(modifiableList));

    modifiableList.add("c");

    exceptionThrown=false;
    try {
        immutableList.add("c");
        fail("add supported for immutableList!!");
    } catch (UnsupportedOperationException e) {
        exceptionThrown=true;
        System.out.println("immutableList.add() not supported");
    }
    assertTrue(exceptionThrown);


    System.out.println("modifiableList:"+modifiableList);
    System.out.println("unModifiableList:"+unModifiableList);
    System.out.println("immutableList:"+immutableList);
    System.out.println("--");

    assertEquals(3, modifiableList.size());
    assertEquals(3, unModifiableList.size());
    assertEquals(2, immutableList.size());

}

Salida

modifiableList:[a]
--
unModifiableList.add() not supported
modifiableList:[a, b]
unModifiableList:[a, b]
--
immutableList.add() not supported
modifiableList:[a, b, c]
unModifiableList:[a, b, c]
immutableList:[a, b]
--
Prashant Bhate
fuente
No puedo ver ninguna diferencia. ¿Puede señalar por qué Immutable es diferente? Puedo ver que tanto Inmutables como inmodificables están arrojando errores y agregar no es compatible. ¿Me estoy perdiendo de algo?
AKS
2
@AKS Por favor, véase el resultado de las últimas tres entradas de la lista después de la adición de 'c' de la lista, mientras que tanto el tamaño de modifiableListy unModifiableListha aumentado immutableListel tamaño no ha cambiado
Prashant Bhate
1
Oh! ¡Entendido! :) .. Así que aquí modificó la lista inmodificable usando los cambios en la lista modificable, pero la lista immutable no puede modificarse. Pero de la misma manera que también puede modificar ImmutableList, creo que aquí el cliente tendrá acceso solo a la referencia de ImmutableList, la referencia a modifiableList, mediante la cual se crea ImmutableList, no estará expuesta al cliente. ¿Derecha?
AKS
1
Sí, porque no hay referencia a new ArrayList<String>(modifiableList)inmutableList no se puede modificar
Prashant Bhate
@PrashantBhate Hola, no hay referencia a new ArrayList<String>(modifiableList)causa de new? Gracias.
Unheilig
11

Creo que la principal diferencia es que el propietario de una colección mutable puede querer proporcionar acceso a la colección a algún otro código, pero proporcionar ese acceso a través de una interfaz que no permite que el otro código modifique la colección (al tiempo que reserva esa capacidad al código de propiedad). Por lo tanto, la colección no es inmutable, pero a ciertos usuarios no se les permite cambiarla.

El tutorial de Java Collection Wrapper de Oracle tiene esto que decir (énfasis agregado):

Los envoltorios no modificables tienen dos usos principales, como sigue:

  • Hacer una colección inmutable una vez que se ha construido. En este caso, es una buena práctica no mantener una referencia a la colección de respaldo. Esto garantiza absolutamente la inmutabilidad.
  • Para permitir que ciertos clientes tengan acceso de solo lectura a sus estructuras de datos. Mantiene una referencia a la colección de respaldo pero entrega una referencia al contenedor. De esta forma, los clientes pueden mirar pero no modificar, mientras usted mantiene el acceso completo .
Michael Burr
fuente
3

Si estamos hablando de JDK Unmodifiable*vs guayaba Immutable*, en realidad la diferencia también está en el rendimiento . Colecciones inmutables pueden ser a la vez más rápido y más memoria eficientes si son no envolturas alrededor de las colecciones regulares (JDK implementaciones son envolturas). Citando al equipo de guayaba :

El JDK proporciona métodos Collections.unmodifiableXXX, pero en nuestra opinión, estos pueden ser

<...>

  • ineficiente: las estructuras de datos todavía tienen toda la sobrecarga de las colecciones mutables, incluidas las comprobaciones de modificaciones concurrentes, el espacio adicional en las tablas hash, etc.
Dmide
fuente
Pensando en el rendimiento, también debe tener en cuenta que un contenedor no modificable no copia la colección, mientras que la versión inmutable utilizada en guayaba y ahora también en jdk9 + con, por ejemplo List.of(...), copia dos veces.
benez
2

Para citar los Tutoriales de Java ™ :

A diferencia de los contenedores de sincronización, que agregan funcionalidad a la colección envuelta, los contenedores no modificables eliminan la funcionalidad. En particular, eliminan la capacidad de modificar la colección interceptando todas las operaciones que modificarían la colección y lanzando una excepción de operación no admitida . Los envoltorios no modificables tienen dos usos principales, como sigue:

  • Hacer una colección inmutable una vez que se ha construido. En este caso, es una buena práctica no mantener una referencia a la colección de respaldo. Esto garantiza absolutamente la inmutabilidad.

  • Para permitir que ciertos clientes tengan acceso de solo lectura a sus estructuras de datos. Mantiene una referencia a la colección de respaldo pero entrega una referencia al contenedor. De esta forma, los clientes pueden mirar pero no modificar, mientras usted mantiene el acceso completo.

(énfasis mío)

Esto realmente lo resume.

Bharat
fuente
1

Como se señaló anteriormente, no modificable no es como inmutable porque una colección no modificable puede modificarse si, por ejemplo, una colección no modificable tiene una colección delegada subyacente a la que hace referencia otro objeto y ese objeto la cambia.

En cuanto a lo inmutable, ni siquiera está bien definido. Sin embargo, generalmente significa que el objeto "no cambiará", pero eso debería definirse de forma recursiva. Por ejemplo, puedo definir inmutable en clases cuyas variables de instancia son todas primitivas y cuyos métodos no contienen argumentos ni primitivas de retorno. Los métodos permiten recursivamente que las variables de instancia sean inmutables y que todos los métodos contengan argumentos que sean inmutables y que devuelvan valores inmutables. Se debe garantizar que los métodos devuelvan el mismo valor con el tiempo.

Suponiendo que podamos hacer eso, también existe el concepto de hilo seguro. Y es posible que te haga creer que inmutable (o que no se puede cambiar con el tiempo) también implica un hilo seguro. Sin embargo, este no es el casoy ese es el punto principal que estoy haciendo aquí que aún no se ha observado en otras respuestas. Puedo construir un objeto inmutable que siempre devuelve los mismos resultados pero no es seguro para subprocesos. Para ver esto, suponga que construyo una colección inmutable manteniendo adiciones y eliminaciones a lo largo del tiempo. Ahora la colección inmutable devuelve sus elementos mirando la colección interna (que puede estar cambiando con el tiempo) y luego (internamente) agregando y eliminando los elementos que se agregaron o eliminaron después de la creación de la colección. Claramente, aunque la colección siempre devolvería los mismos elementos, no es segura para subprocesos simplemente porque nunca cambiará el valor.

Ahora podemos definir inmutables como objetos que son seguros para subprocesos y que nunca cambiarán. Hay pautas para crear clases inmutables que generalmente conducen a tales clases, sin embargo, tenga en cuenta que puede haber formas de crear clases inmutables, que requieren atención a la seguridad de los subprocesos, por ejemplo, como se describe en el ejemplo de colección "instantánea" anterior.

dan b
fuente
1

Los Tutoriales de Java ™ dicen lo siguiente:

A diferencia de los contenedores de sincronización, que agregan funcionalidad a la colección envuelta, los contenedores no modificables eliminan la funcionalidad. En particular, eliminan la capacidad de modificar la colección interceptando todas las operaciones que modificarían la colección y lanzando una excepción de operación no admitida. Los envoltorios no modificables tienen dos usos principales, como sigue:

Hacer una colección inmutable una vez que se ha construido. En este caso, es una buena práctica no mantener una referencia a la colección de respaldo. Esto garantiza absolutamente la inmutabilidad.

Para permitir que ciertos clientes tengan acceso de solo lectura a sus estructuras de datos. Mantiene una referencia a la colección de respaldo pero entrega una referencia al contenedor. De esta forma, los clientes pueden mirar pero no modificar, mientras usted mantiene el acceso completo.

Creo que es una explicación lo suficientemente buena como para entender la diferencia.

piyush121
fuente