Java Immutable Collections
Asked Answered
C

7

129

From Java 1.6 Collection Framework documentation:

Collections that do not support any modification operations (such as add, remove and clear) are referred to as unmodifiable. [...] Collections that additionally guarantee that no change in the Collection object will ever be visible are referred to as immutable.

The second criteria confuses me a bit. Given the first collection is unmodifiable, and assuming that the original collection reference has been disposed away, what are the changes that are referred to in the second line? Is it referring to the changes in the elements held in the collection ie the state of the elements?

Second question:
For a collection to be immutable, how does one go about providing the additional guarantees specified? If the state of an element in the collection is updated by a thread, is it sufficient for immutability that those updates in the state are not visible on the thread holding the immutable collection?

Choiseul answered 10/10, 2011 at 13:12 Comment(1)
In general (especially in functional languages), immutable (aka persistent) collection may change in sense that you can get new state of this collection, but at the same time old state will be still available from other links. For example, newCol = oldCol.add("element") will produce new collection that is copy of old one with 1 more element, and all references to the oldCol will still point to the same unchanged old collection.Elurd
A
173

Unmodifiable collections are usually read-only views (wrappers) of other collections. You can't add, remove or clear them, but the underlying collection can change.

Immutable collections can't be changed at all - they don't wrap another collection - they have their own elements.

Here's a quote from guava's ImmutableList

Unlike Collections.unmodifiableList(java.util.List<? extends T>), which is a view of a separate collection that can still change, an instance of ImmutableList contains its own private data and will never change.

So, basically, in order to get an immutable collection out of a mutable one, you have to copy its elements to the new collection, and disallow all operations.

Accuse answered 10/10, 2011 at 13:16 Comment(4)
if the collection that is wrapped inside another unmodifiable collection does not have any other references to it , then is the unmodifiable collection's bahaviour exactly same to an immutable collection ?Choiseul
@Bozho, If reference to backing collection is not provided, and only unmodifable collection wrapper reference is provided, then there is no way you can change it. Then why do you say "You can't be sure"? Does it point out to a scenario where some thread or somehow it gets modified because backing collection is modifiable?Imperator
@AKS: When a collection is wrapped in unmodifiableList, code which receives only a reference to that list won't be able to modify it, but any code which had a reference to the original list and could modify it before the wrapper was created will still be able to do so afterward. If the code which created the original list knows what has happened to every reference that has ever existed to it, and knows that none of them will fall into the hands of code that might modify the list, then it can know the list will never be modified. If the reference was received from outside code, however...Ecthyma
...there's no way for the unmodifiableList, nor any code that uses it, to know whether or how the wrapped collection might change.Ecthyma
L
94

The difference is that you can't have a reference to an immutable collection which allows changes. Unmodifiable collections are unmodifiable through that reference, but some other object may point to the same data through which it can be changed.

e.g.

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);
Luger answered 10/10, 2011 at 13:16 Comment(1)
what various causes, if any ,can bring a change in a collection that is unmodifiable , provided the original collection reference is not used to modify the underlying collection ?Choiseul
E
22
Collection<String> c1 = new ArrayList<String>();
c1.add("foo");
Collection<String> c2 = Collections.unmodifiableList(c1);

c1 is mutable (i.e. neither unmodifiable nor immutable).
c2 is unmodifiable: it can't be changed itself, but if later on I change c1 then that change will be visible in c2.

This is because c2 is simply a wrapper around c1 and not really an independent copy. Guava provides the ImmutableList interface and some implementations. Those work by actually creating a copy of the input (unless the input is an immutable collection on its own).

Regarding your second question:

The mutability/immutability of a collection does not depend on the mutability/immutability of the objects contained therein. Modifying an object contained in a collection does not count as a "modification of the collection" for this description. Of course if you need a immutable collection, you usually also want it to contain immutable objects.

Erosive answered 10/10, 2011 at 13:19 Comment(8)
@John: no, it's not. At least not according to the definition quoted by the OP.Erosive
@JohnVint , really ? I would not think so, because using c1 , I can still add elements into it , and this addition will be visible to c2. Does that not mean its not immutable ?Choiseul
Well I guess if c1 can escape then it wouldn't be immutable, but immutability also refers to the content within the collection. I was more of referring to an unmodifiableCollection of java.util.Date'sBoff
Sorry though, this context where c1 can be reached somewhere else it wouldnt be truly immutable. However, if c1 was not reachable then it would be.Boff
@Vint: "if c1 does not escape" is not a consideration that's distinguished anywhere in the specification. In my opinion, if you can use the collection in a way that makes it non-immutable, then you should always consider it non-immutable.Erosive
If c2 is global variable, and c1 is defined within a local context of a method, then assigns c2 as Collection.unmodifiableList(c1); which c1 is now unreachable, then yes c2 is immutableBoff
Let me quote the definition: "Collections that additionally guarantee ..." (emphasis mine). Collection.unmodifiableList() can't guarantee that, because it can't guarantee that its argument doesn't escape. Guava ImmutableList.of always produces an immutable List, even if you let its arguments escape.Erosive
We are not disagreeing about the fact of what makes it immutable and not, I was simply stating that because its an unmodifiable collection of an immutable object it can be immutable. Though you are right in this instance because the contents of c1 can change it is not immutableBoff
B
20

Now java 9 has factory Methods for Immutable List, Set, Map and Map.Entry .

In Java SE 8 and earlier versions, We can use Collections class utility methods like unmodifiableXXX to create Immutable Collection objects.

However these Collections.unmodifiableXXX methods are very tedious and verbose approach. To overcome those shortcomings, Oracle corp has added couple of utility methods to List, Set and Map interfaces.

Now in java 9 :- List and Set interfaces have “of()” methods to create an empty or no-empty Immutable List or Set objects as shown below:

Empty List Example

List immutableList = List.of();

Non-Empty List Example

List immutableList = List.of("one","two","three");
Belmonte answered 13/3, 2017 at 6:15 Comment(1)
And in Java 10 List.copyOf and Set.copyOf have been added which allow creating an unmodifiable copy of a list / set, or return the given collection if it is already unmodifiable, see JDK-8191517Tesstessa
C
5

I believe the point here is that even if a collection is Unmodifiable, that does not ensure that it cannot change. Take, for example, a collection that evicts elements if they are too old. Unmodifiable just means that the object holding the reference cannot change it, not that it cannot change. A true example of this is the Collections.unmodifiableList method. It returns an unmodifiable view of a List. The List reference that was passed into this method is still modifiable and so the list can be modified by any holder of the reference that was passed. This can result in ConcurrentModificationExceptions and other bad things.

Immutable means that in no way can the collection be changed.

Second question: An Immutable collection does not mean that the objects contained in the collection will not change, just that collection will not change in the number and composition of objects that it holds. In other words, the collection's list of references will not change. That does not mean that the internals of the object being referenced cannot change.

Clynes answered 10/10, 2011 at 13:20 Comment(2)
Good one!! So if I create a wrapper over Unmodifiable Collection and make modifiable collection reference as private then i can be sure that it is immutable. right?Imperator
If I understand you question, then the answer is no. Wrapping the Unmodifiable does nothing to protect it from having the collection passed to unmodifiableList modified. If you want to do this use ImmutableList.Clynes
B
2

Pure4J supports what you are after, in two ways.

First, it provides an @ImmutableValue annotation, so that you can annotate a class to say that it is immutable. There is a maven plugin to allow you to check that your code actually is immutable (use of final etc.).

Second, it provides the persistent collections from Clojure, (with added generics) and ensures that elements added to the collections are immutable. Performance of these is apparently pretty good. Collections are all immutable, but implement java collections interfaces (and generics) for inspection. Mutation returns new collections.

Disclaimer: I'm the developer of this

Bdellium answered 4/11, 2015 at 13:52 Comment(0)
M
0

Before Java 9, Collections.unmodifiableXXX() methods are used to create unmodifiable collections. These methods just behave like wrapper methods which return unmodifiable view or read-only view of the original collection. i.e you can’t perform modifying operations like add, remove, replace, clear etc through the references returned by these wrapper methods. But, you can modify original collection if you have other references to it and those modifications will be reflected in the view returned by these methods.

For example,

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
 
public class Java9ImmutableCollections 
{
    public static void main(String[] args) 
    {
        List<String> sportList = new ArrayList<String>();
         
        sportList.add("Hockey");
        sportList.add("Cricket");
        sportList.add("Tennis");
         
        List<String> unModifiableSportList = Collections.unmodifiableList(sportList);
 
        System.out.println(sportList);    //Output : [Hockey, Cricket, Tennis]
         
        System.out.println(unModifiableSportList);    //Output : [Hockey, Cricket, Tennis]
         
        unModifiableSportList.add("Wrestling");     //It gives run-time error
         
        sportList.add("Kabaddi");      //It gives no error and will be reflected in unModifiableSportList
         
        System.out.println(sportList);    //Output : [Hockey, Cricket, Tennis, Kabaddi]
         
        System.out.println(unModifiableSportList);    //Output : [Hockey, Cricket, Tennis, Kabaddi]
         
    }
}

From Java 9, static factory methods are introduced to create immutable collections.

1) Immutable List : List.of()
2) Immutable Set : Set.of()
3) Immutable Map : Map.of() or Map.ofEntries()

Immutable Vs Unmodifiable :

Java 9 Immutable collections and unmodifiable collections returned by the Collections.unmodifiableXXX() wrapper methods are not the same. Unmodifiable collections are just the read-only views of the original collection. You can perform modifying operations on the original collection and those modifications will be reflected in the collections returned by these methods. But, immutable collections returned by Java 9 static factory methods are 100% immutable. You can’t modify them once they are created.

Source : https://javaconceptoftheday.com/java-9-immutable-collections/

Mantilla answered 15/9, 2021 at 10:7 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.