Immutable VS Unmodifiable Collection

Immutable vs Unmodifiable collection

An unmodifiable collection is often a wrapper around a modifiable collection which other code may still have access to. So while you can't make any changes to it if you only have a reference to the unmodifiable collection, you can't rely on the contents not changing.

An immutable collection guarantees that nothing can change the collection any more. If it wraps a modifiable collection, it makes sure that no other code has access to that modifiable collection. Note that although no code can change which objects the collection contains references to, the objects themselves may still be mutable - creating an immutable collection of StringBuilder doesn't somehow "freeze" those objects.

Basically, the difference is about whether other code may be able to change the collection behind your back.

Immutable objects and unmodifiable collections

I'd say if you create Immutable collection then you need to protect yourself from somebody modifying list that was given as an argument to constructor, you should protectively copy it.

public Immutable(List<Integer> values, String hello) {
this.values = Collections.unmodifiableList(new ArrayList<Integer>(values));
this.hello = hello;
}

I personally have long ago switched to Guava collections as there you can find interfaces for immutable collections. Your constructor would look like that:

public Immutable(ImmutableList<Integer> values, String hello) {
this.values = values;
this.hello = hello;
}

You'd be sure that argument you receive will not be modified by anyone.

It is usually a good idea to keep reference to immutable list within your immutable class as it then guarantees that you don't modify it by mistake.

Case (1) in my opinion makes sense only when factory method is the only way to create the immutable collection and even then it may break when someone refactors these classes. Unless there are other limitations it is always best to create self-sufficient classes.

What is the difference between google's ImmutableList and Collections.unmodifiableList ()?

No, the immutability is only applied to the amount and references of the objects in the Collection, and does not address the mutability of objects you put in the Collection.

What Immutable list gains over the standard JDK Collections.unmodifiableList is that by using ImmutableList you are guaranteed that the objects referenced, their order and the size of the list cannot change from any source. With Collections.unmodifiableList if something else has a reference to the underlying list, that code can modify the list even though you have a reference to an unmodifiable list.

If, however, you want true immutability, you have to fill the list with immutable objects.

What's the difference between Collections.unmodifiableSet() and ImmutableSet of Guava?

Consider this:

Set<String> x = new HashSet<String>();
x.add("foo");

ImmutableSet<String> guava = ImmutableSet.copyOf(x);
Set<String> builtIn = Collections.unmodifiableSet(x);

x.add("bar");
System.out.println(guava.size()); // Prints 1
System.out.println(builtIn.size()); // Prints 2

In other words, ImmutableSet is immutable despite whatever collection it's built from potentially changing - because it creates a copy. Collections.unmodifiableSet prevents the returned collection from being directly changed, but it's still a view on a potentially-changing backing set.

Note that if you start changing the contents of the objects referred to by any set, all bets are off anyway. Don't do that. Indeed, it's rarely a good idea to create a set using a mutable element type in the first place. (Ditto maps using a mutable key type.)

UnmodifiableMap (Java Collections) vs ImmutableMap (Google)

An unmodifiable map may still change. It is only a view on a modifiable map, and changes in the backing map will be visible through the unmodifiable map. The unmodifiable map only prevents modifications for those who only have the reference to the unmodifiable view:

Map<String, String> realMap = new HashMap<String, String>();
realMap.put("A", "B");

Map<String, String> unmodifiableMap = Collections.unmodifiableMap(realMap);

// This is not possible: It would throw an
// UnsupportedOperationException
//unmodifiableMap.put("C", "D");

// This is still possible:
realMap.put("E", "F");

// The change in the "realMap" is now also visible
// in the "unmodifiableMap". So the unmodifiableMap
// has changed after it has been created.
unmodifiableMap.get("E"); // Will return "F".

In contrast to that, the ImmutableMap of Guava is really immutable: It is a true copy of a given map, and nobody may modify this ImmutableMap in any way.

Update:

As pointed out in a comment, an immutable map can also be created with the standard API using

Map<String, String> immutableMap = 
Collections.unmodifiableMap(new LinkedHashMap<String, String>(realMap));

This will create an unmodifiable view on a true copy of the given map, and thus nicely emulates the characteristics of the ImmutableMap without having to add the dependency to Guava.

Java Immutable Collections

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.

Purpose of Immutable or Unmodifiable Collections

Immutable classes and collections really help to avoid concurrency issues with multiple threads trying to modify the same objects.

Why make a collection unmodifiable?

Take a look at this scenario. There is an application that creates 2 users, and then wants to notify them about something. But only users with name different from Peter should get the notification.

So we have to User.class:

public class User {
private String name;
private Integer id;

public User(final Integer id, final String name) {
this.id = id;
this.name = name;
}
public String getName() {
return name;
}

public Integer getId() {
return id;
}
}

The users are stored in special holder class (containing map):

public class UsersHolder {
private static Map<Integer, User> usersMap = new HashMap<Integer, User>();

public static void addUser(final User user) {
usersMap.put(user.getId(), user);
}

public static Map<Integer, User> getUsersMap() {
return usersMap;
//return Collections.unmodifiableMap(usersMap);
}
}

Then we have the UsersCreator that creates those users and stores them in a map:

public class UsersCreator {
public static void createUsers() {
UsersHolder.addUser(new User(1, "Peter"));
System.out.println("Created user " + UsersHolder.getUsersMap().get(1).getName());
UsersHolder.addUser(new User(2, "Paul"));
System.out.println("Created user " + UsersHolder.getUsersMap().get(2).getName());
}

public static void main(String[] args) {
UsersCreator.createUsers();
System.out.println("Number of users before notification: " + UsersHolder.getUsersMap().size());
new UsersNotificator().notifyAllUsersButPeters(UsersHolder.getUsersMap());
System.out.println("Number of users after notification: " + UsersHolder.getUsersMap().size());
}
}

And the notificator that notifies all but Peters:

public class UsersNotificator {
public void notifyAllUsersButPeters(final Map<Integer, User> map) {
//we don't need peters, so we'll remove them from the list;
Iterator<Entry<Integer, User>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
if (iterator.next().getValue().getName().equals("Peter")) {
iterator.remove();
}
}
//now we can notify all from the list;
notifyUsers(UsersHolder.getUsersMap());
}

private void notifyUsers(Map<Integer, User> map) {
for (final User user : map.values())
System.out.println("notifyingUsers: " + user.getName());
}
}

Now - the notificator was presented with a map and it may modify it, which it does. It doesn't know that it shouldn't modify it as it's global usersMap. In effect it removes all users with name Peter. It does it for it's own purposes, but the results will be visible for every other class using UsersHolder.

The result is as follows:

Created user Peter
Created user Paul
Number of users before notification: 2
notifyingUsers: Paul
Number of users after notification: 1

When returning unmodifiableMap in UsersHolder the removal will not be possible. The only way would be to create new map with users to notify, so our usersHolder is safe.

This example is a bit big, sorry for that, i failed to think of/create somehting shorter.

Unmodifiable map helps to keep your classes Immutable which is safe(as presented in the example) especially in multithreaded enviroment.

Why are the Unmodifiable collections protected to the Collections package?

I think it has to do with the design goals of the framework:

The main design goal was to produce an API that was small in size and, more importantly, in "conceptual weight."

(Source)

You should check out Guava's immutable collection types, if you are willing to learn more conceptual weight :)



Related Topics



Leave a reply



Submit