130

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?

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. Commented Oct 10, 2011 at 13:23

8 Answers 8

179

Unmodifiable collections are usually read-only views (wrappers) of other collections. You can't add to, remove from 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.

Sign up to request clarification or add additional context in comments.

5 Comments

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 ?
@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?
@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...
...there's no way for the unmodifiableList, nor any code that uses it, to know whether or how the wrapped collection might change.
Note that according to the usage of the word immutable and modifiable in the Java 11 docs, immutable means that every object in it is immutable as well and calls that e.g. compare two immutable lists cannot return different results at different points during runtime for immutable collections: docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/… They just differentiate between unmodifiable collections (compare usage here of immutable) and unmodifiable view collections (here called unmodifiable).
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);

1 Comment

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

9 Comments

@John: no, it's not. At least not according to the definition quoted by the OP.
@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 ?
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's
@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.
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.
|
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");

2 Comments

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-8191517
The List.of(...) and copyOf(...) methods are documented as returning "unmodifiable collections" so it is not safe to make blanket assumptions of the results being immutable. The copyOf(...) method even declares that if the input parameter is an unmodifiable list, then it may (or may not) return the original value. I think it would be safer to just assume that strict immutability is not a concept that can be used with the Java Collections Framework.
6

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.

2 Comments

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

Comments

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/

1 Comment

Note that according to the usage of the word immutable and modifiable in the Java 11 docs, immutable means that every object in it is immutable as well and calls that e.g. compare two immutable lists cannot return different results at different points during runtime for immutable collections: docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/… They just differentiate between unmodifiable collections (compare usage here of immutable) and unmodifiable view collections (here called unmodifiable).
0

I am not impressed by answers here.

Unmodifiable collections

As java docs state about Unmodifiable Collections (emphasis mine):

An unmodifiable collection is a collection, all of whose mutator methods (as defined above) are specified to throw UnsupportedOperationException

You can get an unmodifiable list by calling List.copyOf() or List.of().

If you call .removeIf(x -> true) even on an empty list obtained by those methods, the program will throw UnsupportedOperationException.

Unmodifiable view collections

Nor unmodifiable collections are necessarily views. The same Java docs page refers to it separately:

An unmodifiable view collection is a collection that is unmodifiable and that is also a view onto a backing collection

So, a collection has to be both - unmodifiable and a view to qualify as an unmodifiable view. You get unmodifiable views by calling Collections.unmodifiable* methods.

If backing list is unmodifiable, the list obtained by .subList() will also be an unmodifiable view. Unmodifiable views behave and throw exactly the same exceptions as mentioned above.

Immutable collections

Now, to the bonus part. I also like to call them smart collections - it helps to memorize the difference intuitively.

You get them by calling Collections.empty*() methods or Collections.singleton().

They don't have a requirement to throw UnsupportedOperationException (and usually don't) if the execution of its method would have no effect with a given parameter if it was mutable. Let's take a previous example with .removeIf(x -> true) and call it on Collections.emptyList(). Although the list is immutable, there is nothing to remove anyway since the list is empty (even if it was mutable), so it doesn't throw.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.