Question

Suppose there is this code:

List<String> modifiableList = new ArrayList<String>(Arrays.asList("1","2","3"));
List<String> unmodifiableList = Collections.unmodifiableList(modifiableList);
System.out.println(unmodifiableList);
modifiableList.remove("1");
modifiableList.remove("3");
System.out.println(unmodifiableList);

it prints

[1, 2, 3]
[2]

If changing the second line to

List<String> unmodifiableList = Collections.unmodifiableList(
                                       new ArrayList<String>(modifiableList));

it works as expected. The question is why doesn't the UnmodifiableList inner class from Collections (and all the other unmodifiable classes) from there create a fresh copy of the original list, as does the constructor of ArrayList for example in order to really make it unmodifiable?

Edit: I understand what the code does; my question is why was it implemented this way? Why does the constructor from the UnmodifiableList (inner class from Collections) behave like the constructor of ArrayList in creating a fresh copy of the underlying array? Why a modifiable collection (ArrayList) copies the whole content while an unmodifiable collection doesn't?

Was it helpful?

Solution

Well the purpose of the methods is to create an unmodifiable view on an existing collection. That's the documented behaviour, and in many cases it's exactly what you want - it's much more efficient than copying all the data, for example... or you want to hand callers collections which will reflect any changes you want to make, but without allowing them to make changes.

If you want an immutable copy of the data (or at least the references...) then just create a copy and then create an immutable view over the top of it - just as you are.

So basically, you can easily create either a view or a copy-and-view, based on Collections.unmodifiable* themselves performing the view operation. So we have two orthogonal operations:

  • Create a copy (e.g. via the constructor)
  • Create a view (via Collections.unmodifiable*)

Those operations can be composed very easily. If Collections.unmodifiable* actually performed a "copy only" then we'd require other operations in order to just make a view. If you accept that both options are useful in different situations, making them composable gives lots of flexibility.

OTHER TIPS

The reason is simple efficiency. Copying all of the elements of a collection could be very time-consuming, particularly if the collection being wrapped has some sort of magic going on like JPA lazy-loading, and requires extra memory. Wrapping the underlying collection as-is is a trivial operation that imposes no additional overhead. In the case where the developer really does want a separate copy (unmodifiable or not), it's very easy to create it explicitly. (I tend to use Guava Immutable* for this.)

Please note, that unmodifiableList returns a "unmodifiable view" of provided list. So the list itself stays at it is (it can be still modified), only its "unmodifiable view" is unmodifiable. You can think of it as of SQL tables and views --- you can run DML scripts on tables and it will be reflected on related views. As to ArrayList --- it's backed by... an array, so it's implementation feature, that it copies elements from provided source list (which doesn't have to be backed by an array actually). Does it answer your question?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top