As per the doc the method
Collections.unmodifiableList returns an unmodifiable view of the specified list. Is the list returned truly unmodifiable? What do we mean by unmodifiable view?
As per doc the method
Collectors.toUnmodifiableList returns a Collector that accumulates the input elements into an unmodifiable List in encounter order. Is the list returned here truly unmodifiable?
Note: By modifiable I mean the view can be modified by using
set operation. I want to understand the difference and how are they related?
Collections.unmodifiableList returns an unmodifiable view of the specified list. An unmodifiable view collection is a collection that is unmodifiable and is also a view onto a backing collection. Note that changes to the backing collection might still be
possible, and if they occur, they are visible through the unmodifiable view.
List<String> srcList = Arrays.asList("Apple", "Banana", "Cherry"); var fruits = new ArrayList<>(srcList); var unmodifiableList = Collections.unmodifiableList(fruits); fruits.set(0, "Apricot"); var modFruit = unmodifiableList.get(0); System.out.println(modFruit); // prints Apricot
We can have a true immutable list in Java 10 and later. There are two ways to get truly unmodifiable list as shown below:
var unmodifiableList = List.copyOf(srcList);=> prints Apple
var unmodifiableList = srcList.stream().collect(Collectors.toUnmodifiableList());=> prints Apple
So the method
Collectors.toUnmodifiableList returns a true unmodifiable list
List.of introduced in Java 9. This method returns a
Collector where as the method
Collections.unmodifiableList returns a list. As per doc the unmodifiable lists has following characteristics:
- They are unmodifiable. Elements cannot be added, removed, or replaced. Calling any mutator method on the List will always cause
UnsupportedOperationExceptionto be thrown. However, if the contained
elements are themselves mutable, this may cause the List’s contents to
appear to change.
- They disallow null elements. Attempts to create them with null elements result in
- They are serializable if all elements are serializable.
- The order of elements in the list is the same as the order of the provided arguments, or of the elements in the provided array.
- They are
value-based. Callers should make no assumptions about the identity of the returned instances. Factories are free to create new
instances or reuse existing ones. Therefore, identity-sensitive
operations on these instances (reference equality (==), identity hash
code, and synchronization) are unreliable and should be avoided.
- They are serialized as specified on the Serialized Form page.
Collections.unmodifiableList returns an
unmodifiable view, which means calling any methods of the returned
List that mutate the
List would throw
UnsupportedOperationException. However, the original
List that was passed to that method can still be modified (assuming it is modifiable), and such modifications will be reflected in the
List returned by
Collections.unmodifiableList. Therefore, it can only be considered “truly unmodifiable” if you don’t have access to the original
Collectors.toUnmodifiableList, on the other hand, is used to generate a new
List which is unmodifiable, so there is no way to modify that
List. Therefore it is “truly unmodifiable”.
As a side note,
Collectors.toUnmodifiableList just uses the implementation of
List::of that was added in java-9, the finisher for that implementation is:
list -> (List<T>)List.of(list.toArray()
list is just an
ArrayList; so your question just boils down to what is the difference between
Collections::unmodifiableList (of course with the caveat that this is not specified and happens under the current implementation) and while I could detail some of the differences, there are already good ones (and many more if you search).
One point to note is how these different types handle
nulls (the other point that one s actually a view, was already provided in the other answers):
List<Integer> left = Stream.of(1, 2, 3) .collect(Collectors.collectingAndThen( Collectors.toList(), Collections::unmodifiableList)); System.out.println(left.contains(null)); List<Integer> right = Stream.of(1, 2, 3) .collect(Collectors.toUnmodifiableList()); System.out.println(right.contains(null));
The last line of code will throw an Exception, you might not expect that at all.