Ashish Sood
Ashish Sood

Reputation: 33

Why make a collection unmodifiable?

I know how to make a collection unmodifiable in java but I dont understand the need for such a method to exist. Can someone please explain this with an example scenario where I would want to make my collection unmodifiable?

Thanks in advance

Upvotes: 1

Views: 379

Answers (4)

Kitalda
Kitalda

Reputation: 351

There are many situations in which you do not want your collection to be modifiable. Whenever you know that the collection is initialized with exactly the content it should contain at all times, it can provide security to make it unmodifiable.

The (rather long) example provided by another user is a good example of where it often causes problems. Whenever you traverse a collection, there is a risk you change the collection if you forget to do it on a copy. Making the collection unmodifiable catches and prevents this easy to make mistake.

Upvotes: 0

Michał Schielmann
Michał Schielmann

Reputation: 1382

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.

Upvotes: 2

m4rtin
m4rtin

Reputation: 2475

An unmodifiable collection is basically read-only which is exactly what you want in case you need to publish such collection to client code and you don't want the client code to modify the collection.

It also promotes immutability which is generally a good thing since you won't have to care about the state of the collection for the rest of the execution of your program. See item 15 of Effective Java (2nd Edition) : Minimize mutability, and to quote Joshua Bloch :

Immutable objects are simple. An immutable object can be in exactly one state, the state in which it was created.

Note that an unmodifiable collection will not make the contained objects immutable. This is a property each of the contained objects needs to make sure of, if it is required of course.

Upvotes: 4

Mark Bolusmjak
Mark Bolusmjak

Reputation: 24409

The most efficient way to share private data outside of a class is to simply return it. But then something outside of the class can change the data that the class depends on. Another option is to copy the data before you share. This takes time and memory to do. Unmodifiable collections will often wrap the data and simply present it without allowing an outside class to edit it. This is faster than making a copy. An outside class can optionally make a modifiable copy if it needs.

Upvotes: 8

Related Questions