Colecciones inmutables de Java

115

De la documentación de Java 1.6 Collection Framework :

Las colecciones que no admiten ninguna operación de modificación (como add, removey clear) se denominan no modificables . [...] Las colecciones que además garantizan que ningún cambio en el objeto de la colección será visible se denominan inmutables .

El segundo criterio me confunde un poco. Dado que la primera colección no se puede modificar y suponiendo que la referencia de la colección original se haya eliminado, ¿a qué cambios se hace referencia en la segunda línea? ¿Se refiere a los cambios en los elementos contenidos en la colección, es decir, al estado de los elementos?

Segunda pregunta:
Para que una colección sea inmutable, ¿cómo se hace para proporcionar las garantías adicionales especificadas? Si el estado de un elemento de la colección es actualizado por un hilo, ¿es suficiente para la inmutabilidad que esas actualizaciones en el estado no sean visibles en el hilo que contiene la colección inmutable?

Para que un cobro sea inmutable, ¿cómo se hace para brindar las garantías adicionales especificadas?

Bhaskar
fuente
En general (especialmente en lenguajes funcionales), la colección inmutable (también conocida como persistente) puede cambiar en el sentido de que puede obtener un nuevo estado de esta colección, pero al mismo tiempo , el estado antiguo seguirá estando disponible en otros enlaces. Por ejemplo, newCol = oldCol.add("element")producirá una nueva colección que es copia de la antigua con 1 elemento más, y todas las referencias a la misma oldColseguirán apuntando a la misma colección antigua sin cambios.
ffriend

Respuestas:

154

Las colecciones no modificables suelen ser vistas de solo lectura (envoltorios) de otras colecciones. No puede agregarlos, eliminarlos o borrarlos, pero la colección subyacente puede cambiar.

Las colecciones inmutables no se pueden cambiar en absoluto, no envuelven otra colección, tienen sus propios elementos.

Aquí hay una cita de guayaba ImmutableList

A diferencia de Collections.unmodifiableList(java.util.List<? extends T>), que es una vista de una colección separada que aún puede cambiar, una instancia de ImmutableListcontiene sus propios datos privados y nunca cambiará.

Entonces, básicamente, para obtener una colección inmutable de una mutable, debe copiar sus elementos a la nueva colección y no permitir todas las operaciones.

Bozho
fuente
@Bhaskar - vea mi último párrafo.
Bozho
1
Si la colección que está envuelta dentro de otra colección inmodificable no tiene ninguna otra referencia a ella, ¿el comportamiento de la colección inmodificable es exactamente el mismo que el de una colección inmutable?
Bhaskar
1
@Bozho, si no se proporciona una referencia a la colección de respaldo, y solo se proporciona una referencia de envoltorio de colección no modificable, entonces no hay forma de que pueda cambiarla. Entonces, ¿por qué dices "No puedes estar seguro"? ¿Señala un escenario en el que algún hilo o de alguna manera se modifica porque la colección de respaldo es modificable?
AKS
@AKS: cuando se incluye una colección unmodifiableList, el código que recibe solo una referencia a esa lista no podrá modificarla, pero cualquier código que tuviera una referencia a la lista original y pudiera modificarla antes de que se creara la envoltura seguirá siendo poder hacerlo después. Si el código que creó la lista original sabe lo que ha sucedido con cada referencia que ha existido a él, y sabe que ninguno de ellos caerá en manos de un código que pueda modificar la lista, entonces puede saber que la lista nunca será modificado. Sin embargo, si la referencia se recibió de un código externo ...
supercat
... no hay forma de que unmodifiableList, ni ningún código que lo use, sepa si la colección empaquetada podría cambiar o cómo.
supercat
86

La diferencia es que no puede tener una referencia a una colección inmutable que permita cambios. Las colecciones inmodificables no son modificables a través de esa referencia. , pero algún otro objeto puede apuntar a los mismos datos a través de los cuales se puede cambiar.

p.ej

List<String> strings = new ArrayList<String>();
List<String> unmodifiable = Collections.unmodifiableList(strings);
unmodifiable.add("New string"); // will fail at runtime
strings.add("Aha!"); // will succeed
System.out.println(unmodifiable);
Simon Nickerson
fuente
1
¿Qué diversas causas, si las hay, pueden provocar un cambio en una colección que no se puede modificar, siempre que la referencia de la colección original no se utilice para modificar la colección subyacente?
Bhaskar
21
Collection<String> c1 = new ArrayList<String>();
c1.add("foo");
Collection<String> c2 = Collections.unmodifiableList(c1);

c1es mutable (es decir, ni inmodificable ni inmutable ). no
c2 es modificable : no se puede cambiar en sí mismo, pero si luego cambio c1, ese cambio será visible en c2.

Esto se debe a que c2es simplemente un envoltorio c1y no realmente una copia independiente. Guava proporciona la ImmutableListinterfaz y algunas implementaciones. Estos funcionan creando una copia de la entrada (a menos que la entrada sea una colección inmutable por sí misma).

Respecto a tu segunda pregunta:

La mutabilidad / inmutabilidad de una colección no depende de la mutabilidad / inmutabilidad de los objetos que contiene. La modificación de un objeto contenido en una colección no cuenta como una "modificación de la colección" para esta descripción. Por supuesto, si necesita una colección inmutable, generalmente también desea que contenga objetos inmutables.

Joachim Sauer
fuente
2
@John: no, no lo es. Al menos no según la definición citada por el PO.
Joachim Sauer
@JohnVint, ¿de verdad? No lo creo, porque usando c1, todavía puedo agregarle elementos, y esta adición será visible en c2. ¿No significa eso que no es inmutable?
Bhaskar
Bueno, supongo que si c1 puede escapar, entonces no sería inmutable, pero la inmutabilidad también se refiere al contenido dentro de la colección. Me refería más a una colección no modificable de java.util.Date's
John Vint
1
@Vint: "si c1no se escapa" no es una consideración que se distinga en ninguna parte de la especificación. En mi opinión, si se puede usar la colección de una manera que hace que no sea inmutable, entonces usted debe siempre tener en cuenta que no inmutable.
Joachim Sauer
1
Permítanme citar la definición: "Colecciones que además garantizan ..." (énfasis mío). Collection.unmodifiableList() no puede garantizar eso, porque no puede garantizar que su argumento no se escape. La guayaba ImmutableList.of siempre produce un inmutable List, incluso si dejas escapar sus argumentos.
Joachim Sauer
17

Ahora java 9 tiene métodos de fábrica para lista inmutable, conjunto, mapa y entrada de mapa.

En Java SE 8 y versiones anteriores, podemos usar métodos de utilidad de la clase Colecciones como unmodifiableXXX para crear objetos de colección inmutables.

Sin embargo, estos métodos Collections.unmodifiableXXX son un enfoque muy tedioso y detallado. Para superar esas deficiencias, Oracle corp ha agregado un par de métodos de utilidad a las interfaces List, Set y Map.

Ahora en java 9: ​​- Las interfaces List y Set tienen métodos "of ()" para crear una lista inmutable vacía o no vacía o objetos Set como se muestra a continuación:

Ejemplo de lista vacía

List immutableList = List.of();

Ejemplo de lista no vacía

List immutableList = List.of("one","two","three");
Opster Elasticsearch Pro-Vijay
fuente
2
Y en Java 10 List.copyOfy Set.copyOfse han agregado que permiten crear una copia no modificable de una lista / conjunto, o devolver la colección dada si ya no es modificable, consulte JDK-8191517
Marcono1234
6

Creo que el punto aquí es que incluso si una colección no se puede modificar, eso no garantiza que no pueda cambiar. Tomemos, por ejemplo, una colección que desaloja elementos si son demasiado antiguos. No modificable solo significa que el objeto que contiene la referencia no puede cambiarlo, no que no pueda cambiar. Un verdadero ejemplo de esto esCollections.unmodifiableList método. Devuelve una vista no modificable de una lista. La referencia de la lista que se pasó a este método aún es modificable, por lo que cualquier titular de la referencia que se pasó puede modificar la lista. Esto puede resultar en ConcurrentModificationExceptions y otras cosas malas.

Inmutable, significa que de ninguna manera se puede cambiar la colección.

Segunda pregunta: Una colección inmutable no significa que los objetos contenidos en la colección no cambiarán, solo que la colección no cambiará en el número y composición de los objetos que contiene. En otras palabras, la lista de referencias de la colección no cambiará. Eso no significa que las partes internas del objeto al que se hace referencia no puedan cambiar.

Juan B
fuente
Buena !! Entonces, si creo un contenedor sobre una colección no modificable y hago una referencia de colección modificable como privada, puedo estar seguro de que es inmutable. ¿Derecha?
AKS
Si entiendo su pregunta, entonces la respuesta es no. Envolver el inmodificable no hace nada para protegerlo de que la colección pase a unmodifiableListmodificarse. Si quieres hacer este uso ImmutableList.
John B
0

Pure4J respalda lo que busca , de dos maneras.

Primero, proporciona una @ImmutableValueanotación, para que pueda anotar una clase para decir que es inmutable. Hay un complemento de Maven que le permite verificar que su código realmente sea inmutable (uso de, finaletc.).

En segundo lugar, proporciona las colecciones persistentes de Clojure (con genéricos añadidos) y garantiza que los elementos añadidos a las colecciones sean inmutables. El rendimiento de estos es aparentemente bastante bueno. Las colecciones son inmutables, pero implementan interfaces de colecciones Java (y genéricos) para su inspección. Mutation devuelve nuevas colecciones.

Descargo de responsabilidad: soy el desarrollador de esto

Rob Moffat
fuente