Regarding unmodifiable collection - java

Consider the following code below
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<Integer> intList1=new ArrayList<Integer>();
List<Integer> intList2;
intList1.add(1);
intList1.add(2);
intList1.add(3);
intList2=Collections.unmodifiableList(intList1);
intList1.add(4);
for(int i=0;i<4;i++)
{
System.out.println(intList2.get(i));
}
}
}
The result of the above code is
1
2
3
4
In the above code we create an unmodifiable List intList2 from the contents of the List intList1. But after the Collections.unmodifiable statement when I make a change to intList1 that change reflects to intList2. How is this possible ?

You need to read the Javadoc for Collections.unmodifiableList
Returns an unmodifiable view of the specified list.
This means that the returned view is unmodifiable. If you have the original reference you can change the collection. If you change the collection then changes will be reflected in the view.
This has the advantage of being very fast, i.e. you don't need to copy the collection, but the disadvantage that you noted - the resulting collection is a view.
In order to create a truly unmodifiable collection you would need to copy then wrap:
intList2=Collections.unmodifiableList(new ArrayList<>(intList1));
This copies the contents of intList1 into another collection then wraps that collection in the unmodifiable variable. No reference to the wrapped collection exists.
This is expensive - the entire underlying datastore (an array in this case) needs to duplicated which (generally) takes O(n).
Google Guava provides immutable collections which solve some of the problems of making defensive copies:
If a collection is already immutable it is not copied again
Provide an interface which can be used to explicitly state that a collection is immutable
Provide numerous static factory methods to generate immutable collections
But speed is still the key concern when using immutable copies of collections rather than unmodifiable views.
It should be noted that the usual use for Collections.unmodifiableXXX is to return from a method, for example a getter:
public Collection<Thing> getThings() {
return Collections.unmodifiableCollection(things);
}
In this case there are two things to note:
The user of getThings cannot access things so the unmodifiability cannot be broken.
It would be very expensive to copy things each time a getter were called.
In summary the answer to your question is a little more complex than you might have expected and there are a number of aspects to consider when passing collections around in your application.

From the Javadoc of Collections.unmodifiableList:
Returns an unmodifiable view of the specified list. This method allows modules to provide users with "read-only" access to internal lists.
It prevent the returned list to be modified, but the original list itself still can be.

In your code you are
intList2=Collections.unmodifiableList(intList1);
creating unmodifiableList in intList2. So you are free to make changes in inList1
but you are not allowed to do any changes in intList2
try this:
intList2.add(4);
you will get
java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableCollection.add(Unknown Source)
above exception.

Collections.unmodifiableList returns a "read-only" view of the internal list. While the object that was returned is not modifiable the original list that it references can be modified. Both objects point to the same object in memory so it will reflect changes made.
Here is a good explanation of what is happening.

That happens because the unmodifiable list is internally backed for the first list, if you really want it to be unmodifiable you shouldn't use the first List any more.

Related

Why is it better to return unmodifiableList from a method where list is created?

public List<Integer> getInts()
{
List<Integer> xs = new ArrayList<Integer>();
xs.add(1);
// return Collections.unmodifiableList(xs);
return xs;
}
I understand that returning an unmodifiable list will prevent the consumer from adding additional elements to the list reference but apart from that what am I gaining here by wrapping it in an unmodifiable list? I am creating a new list every time the method is invoked.
The answer from Bohemian states some good general principles about returning unmodifiable, immutable, or defensively-copied data, in order to preserve encapsulation. This is certainly true if the data is internal to the object, e.g., stored in a field.
However, the OP stated that the returned list is newly created each time the method is called. In this case, why return an unmodifiable list instead of a regular ArrayList? There are reasons, but they're somewhat subtle, and they have less to do with encapsulation that with preserving implementation flexibility.
As a background topic, you need to decide whether this API has any long-term compatibility constraints or policies on it. If you return a mutable list, then it's possible (according to Hyrum's Law) that callers will come to depend on its mutability. (Hyrum's Law states essentially that any observable property of a system will eventually be depended upon by users.) Personally I think that mutating a collection that's been returned to you is sloppy programming, but the fact is, people do it. If this were the case, and in the future you were to propose changing the returned list to be unmodifiable, would that be prohibited because it's incompatible and it would break some callers? If you don't care about compatibility (and some projects don't) then maybe it doesn't matter. But if you do, then you should think about returning an unmodifiable list now.
One reason (mentioned by others in comments) is that you might decide not to create a fresh list every time, but you might cache it and return it to several callers. If you do this, it should definitely be made unmodifiable, to prevent one caller from modifying the list and affecting all of them.
Another reason is that different list implementations have different performance and space characteristics. The Collections.singletonList and List.of(x) implementations store their single element in a field of the List object itself, whereas ArrayList stores its elements -- even if there's only one -- in a separate array object. The small list implementations can save considerable space
compared to ArrayList, if you're creating a lot of them. Returning an unmodifiable wrapper around an ArrayList will ease compatibility concerns if you want to switch to a singleton list or the Java 9 unmodifiable list implementations in the future.
You might also want to add some adaptive behavior to the method, e.g., depending on the number of elements in the returned list. For example,
if (count == 0) {
return Collections.emptyList(); // eventually, List.of()
} else if (count == 1) {
return Collections.singletonList(i); // eventually, List.of(i)
} else {
List<Integer> list = new ArrayList<>();
// populate list
return Collections.unmodifiableList(list);
}
If you don't wrap the ArrayList in an unmodifiable wrapper, callers will be exposed to odd differences in behavior, such as the list sometimes being modifiable and sometimes not. If possible, it's best to provide uniform behavior in all cases, thereby preserving implementation flexibility for future changes.
When an object returns a mutable private field, it is exposed to unwanted/unknown alteration of its internal state by external agents, which violates encapsulation.
The pattern to which you refer, called safe publishing, protects against this problem. It can be accomplished by returning either a deep copy, or the field (and any mutable sub-fields) in an immutable wrapper.
In the case of your commented out code:
return Collections.unmodifiableList(xs);
it is creating a new object, but it just a very thin layer on top of the list and you would barely be able to measure the cpu or memory performance cost of doing it.
You can also:
return new ArrayList<>(xs);
to make a deep copy (in this case, because Integer is immutable).

Why don't the unmodifiable methods from Collections class, create collections with new elements?

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

What is a view of a collection?

I've been reading the term view a few times when using Guava collections and reading its documentation.
I've looked for an explanation of what a view is in this context and whether it's a term used outside of Guava. It's quite often used here. This type from Guava has view in its name.
My guess is that a view of a collection is another collection with the same data but structured differently; for instance when I add the entries from a java.util.HashSet to a java.util.LinkedHashSet the latter would be a view of the former. Is that correct?
Can somebody hook me up with a link to an accepted definition of view, if there is one?
Thanks.
A view of another object doesn't contain its own data at all. All of its operations are implemented in terms of operations on the other object.
For example, the keySet() view of a Map might have an implementation that looks something like this:
class KeySet implements Set<K> {
private final Map<K, V> map;
public boolean contains(Object o) {
return map.containsKey(o);
}
...
}
In particular, whenever you modify the backing object of your view -- here, the Map backs the keySet() -- the view reflects the same changes. For example, if you call map.remove(key), then keySet.contains(key) will return false without you having to do anything else.
Alternately, Arrays.asList(array) provides a List view of that array.
String[] strings = {"a", "b", "c"};
List<String> list = Arrays.asList(strings);
System.out.println(list.get(0)); // "a"
strings[0] = "d";
System.out.println(list.get(0)); // "d"
list.set(0, "e");
System.out.println(strings[0]); // "e"
A view is just another way of looking at the data in the original backing object -- Arrays.asList lets you use the List API to access a normal array; Map.keySet() lets you access the keys of a Map as if it were a perfectly ordinary Set -- all without copying the data or creating another data structure.
Generally, the advantage of using a view instead of making a copy is the efficiency. For example, if you have an array and you need to get it to a method that takes a List, you're not creating a new ArrayList and a whole copy of the data -- the Arrays.asList view takes only constant extra memory, and just implements all the List methods by accessing the original array.
A view in this context is a collection backed by another collection (or array) that itself uses a constant amount memory (i.e. the memory does not depend on the size of the backing collection). Operations applied to the view are delegated to the backing collection (or array). Of course it's possible to expand this definition beyond just collections but your question seems to pertain specifically to them.
For example, Arrays.asList() returns "a list view of the specified array". It does not copy the elements to a new list but rather creates a list that contains a reference to the array and operates based on that.
Another example is Collections.unmodifiableList() which returns "an unmodifiable view of the specified list". In other words, it returns a list containing a reference to the specified list to which all operations are delegated. In this case, the list returned does not permit you to modify it in any way, and so instead of delegating methods responsible for mutating the list, it throws an exception when such methods are called instead.

subList view based on value of elements

I know that the Collection framework allows for the creation of "views", that is lightweight "wrappers" for a Collection object.
What I am especially interested in is, given a List, to return a view for only a subset of elements matching some conditions.
Basically, what I want to emulate is the functionality of the subList() method, only not based on start and end indexes, but on some parameters of the elements.
The first approach I thought about was simply to create another List, go through the first List and check each element...
While this wouldn't be actually copy any MyObject but only their references, I would anyways create a new List object, with its overhead. Isn't that right?
Is there any lightweight method of doing what I need?
N.B. My original List is a really big collection...
Thank you all
You can do this easily in Java using the Guava collections (Collections2 has a filter method http://docs.guava-libraries.googlecode.com/git-history/v11.0.1/javadoc/index.html).
You can also do this in groovy using the findAll method, for example
myList.findAll { it.contains("aValue") }
Any of these methods will create a new collection under the hood. So they are just doing the work for you of iterating over the elements and checking them. The overhead of creating a new list is minimal (it's just instantiating one new object).
I would anyways create a new List object, with its overhead
I don't understand what your concern here. Looking at source of ArrayList class even subList(int fromIndex, int toIndex) method in List class creates a new inner class (which extends from List). That is essentially what you will be doing in your method i.e. create a new List instance and copy your matching element's reference into it. That custom method will be more or less will have same performance as subList method.

Does Collections.unmodifiableCollection(list) copy the collection?

private List list;
If we use Collections.unmodifiableCollection(list), will this return a copy of the collection, or is it faster than creating a copy? We could do other.addAll(list) but we have list of 600,000 object, so addAll is not so good.
Caller just needs a read-only collection.
Collections.unmodifiableList just returns an unmodifiable wrapper; it does not copy the contents of the input list.
Its Javadoc states this fairly clearly:
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.
As Matt Ball mentioned, if you don't need the internal List to be mutable, you may want to just store a Guava ImmutableList internally... you can safely give that to callers directly since it can never change.
Does Collections.unmodifiableCollection(list) copy the collection?
The other answers are correct (+1s all around): the answer is no.
Instead of Collections.unmodifiableList() you can use Guava's ImmutableList.copyOf() to create an immutable (not modifiable) list copy.
Collections.unmodifiableCollection(..) simply wraps the original collection, disabling methods for modification. It doesn't copy it.
If you change the original list, the "unmodifiable" collection will also change. But the client having only the unmodifiable collection can't change it.

Categories