127

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

For a collection to be immutable, how does one go about providing the additional guarantees specified?

Rudy Vissers
  • 5,267
  • 4
  • 35
  • 37
Bhaskar
  • 7,443
  • 5
  • 39
  • 51
  • 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. – ffriend Oct 10 '11 at 13:23

7 Answers7

170

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.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • 3
    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 ? – Bhaskar Oct 10 '11 at 15:11
  • 1
    @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 Aug 20 '13 at 17:25
  • @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... – supercat Dec 26 '13 at 17:33
  • ...there's no way for the `unmodifiableList`, nor any code that uses it, to know whether or how the wrapped collection might change. – supercat Dec 26 '13 at 17:34
  • but immutable collections stops mutations only at runtime right? .. at compile time you could still attempt mutating operations on it?.. is that right? – rogue-one Oct 31 '21 at 05:46
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);
Simon Nickerson
  • 42,159
  • 20
  • 102
  • 127
  • 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 ? – Bhaskar Oct 10 '11 at 14:52
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.

diziaq
  • 6,881
  • 16
  • 54
  • 96
Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • 2
    @John: no, it's not. At least not according to the definition quoted by the OP. – Joachim Sauer Oct 10 '11 at 13:39
  • @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 ? – Bhaskar Oct 10 '11 at 13:42
  • 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 – John Vint Oct 10 '11 at 14:20
  • 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. – John Vint Oct 10 '11 at 14:21
  • 1
    @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. – Joachim Sauer Oct 10 '11 at 14:22
  • 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 immutable – John Vint Oct 10 '11 at 14:23
  • 1
    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. – Joachim Sauer Oct 10 '11 at 14:25
  • 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 immutable – John Vint Oct 10 '11 at 14:27
19

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");
Vijay
  • 4,694
  • 1
  • 30
  • 38
  • 4
    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](https://bugs.openjdk.java.net/browse/JDK-8191517) – Marcono1234 Nov 24 '19 at 15:21
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 Collections.unmodifiableList method. It returns an unmodifiable view of a List. The 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, mean 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.

John B
  • 32,493
  • 6
  • 77
  • 98
  • 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? – AKS Aug 20 '13 at 17:29
  • 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`. – John B Aug 20 '13 at 17:52
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

Rob Moffat
  • 465
  • 5
  • 11
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/

user2485429
  • 567
  • 8
  • 7