Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my `unmodifiableList` modifiable? [duplicate]

I want a List whose elements cannot be removed nor added. I thought I'd found the answer with Collections.unmodifiableList in Java 8. I pass my original list and get back a supposedly unmodifiable list.

Yet when I delete an element from the original list, my unmodifiable list is modified. What is going on?

See this demo code. My unmodifiable list shrinks from 3 elements 2 when deleting from original.

String dog = "dog";
String cat = "cat";
String bird = "bird";

List< String > originalList = new ArrayList<>( 3 );
originalList.add( dog );
originalList.add( cat );
originalList.add( bird );

List< String > unmodList = Collections.unmodifiableList( originalList );
System.out.println( "unmod before: " + unmodList );  // Yields [dog, cat, bird]
originalList.remove( cat );  // Removing element from original list affects the unmodifiable list?
System.out.println( "unmod after: " + unmodList );  // Yields [dog, bird]
like image 752
Basil Bourque Avatar asked Sep 02 '15 20:09

Basil Bourque


People also ask

How do you make an unmodifiableList modifiable in Java?

The solution to this problem is quite simple and is highlighted in the following code. final List<String> modifiable = new ArrayList<>(); modifiable. add("Java"); modifiable. add("is"); // Here we are creating a new array list final List<String> unmodifiable = Collections.

What is collections unmodifiableList?

The unmodifiableList() method of java. util. Collections class is used to return an unmodifiable view of the specified list. This method allows modules to provide users with “read-only” access to internal lists.

Is Unmodifiable list immutable?

If you create a List and pass it to the Collections. unmodifiableList method, then you get an unmodifiable view. The underlying list is still modifiable, and modifications to it are visible through the List that is returned, so it is not actually immutable.


1 Answers

The unmodifiableList Is Backed By Original List

That unmodifiableList method in Collections utility class does not create a new list, it creates a pseudo-list backed by the original list. Any add or remove attempts made through the "unmodifiable" object will be blocked, thus the name lives up to its purpose. But indeed, as you have shown, the original list can be modified and simultaneously affects our secondary not-quite-unmodifiable list.

This is spelled out in the class documentation:

Returns an unmodifiable view of the specified list. This method allows modules to provide users with "read-only" access to internal lists. Query operations on the returned list "read through" to the specified list, and attempts to modify the returned list, whether direct or via its iterator, result in an UnsupportedOperationException.

That fourth word is key: view. The new list object is not a fresh list. It is an overlay. Just like tracing paper or transparency film over a drawing stops you from making marks on the drawing, it does not stop you from going underneath to modify the original drawing.

Moral of the Story: Do not use Collections.unmodifiableList for making defensive copies of lists.

Ditto for Collections.unmodifiableMap, Collections.unmodifiableSet, and so on.

Google Guava

Instead of the Collections class, for defensive programming I recommend using the Google Guava library and its ImmutableCollections facility.

You can make a fresh list.

public static final ImmutableList<String> ANIMALS = ImmutableList.of(
        dog,
        cat,
        bird );

Or you can make a defensive copy of an existing list. In this case you will get a fresh separate list. Deleting from the original list will not affect (shrink) the immutable list.

ImmutableList<String> ANIMALS = ImmutableList.copyOf( originalList ); // defensive copy!

But remember, while the collection’s own definition is separate, the contained objects are shared by both the original list and new immutable list. When making that defensive copy, we are not duplicating the "dog" object. Only one dog object remains in memory, both lists contain a reference pointing to the same dog. If the properties in the "dog" object are modified, both collections are pointing to that same single dog object and so both collections will see the dog’s fresh property value.

like image 191
Basil Bourque Avatar answered Nov 14 '22 21:11

Basil Bourque