When is the unmodifiablemap (really) necessary?

32,669

Solution 1

Collections.unmodifiableMap guarantees that the map will not be modified. It's mostly useful if you want to return a read-only view of an internal map from a method call, e.g:

class A {
    private Map importantData;

    public Map getImportantData() {
        return Collections.unmodifiableMap(importantData);
    }
}

This gives you a fast method that does not risk the client changing your data. It's much faster and more memory efficient than returning a copy of the map. If the client really does want to modify the returned value then they can copy it themselves, but changes to the copy won't be reflected in A's data.

If you are not returning map references to anyone else then don't bother making it unmodifiable unless you are paranoid about making it immutable. You can probably trust yourself to not change it.

Solution 2

Cameron Skinner's statement above that "Collections.unmodifiableMap guarantees that the map will not be modified" is actually only partly true in general, although it happens to be accurate for the specific example in the question (only because the Character object is immutable). I'll explain with an example.

Collections.unmodifiableMap actually only gives you protection that the references to objects held in the map cannot be changed. It does that by restricting the 'put' into the map that it returns. However, the original encapsulated map can still be modified from outside the class because Collections.unmodifiableMap does not make any copies of the contents of the map.

In the question posted by Paulo, the Character objects held in the map are luckily unmodifiable. However, in general this may not be true and the unmodifiability advertised by Collections.unmodifiableMap should not be the only safeguard. For instance, see the example below.

import java.awt.Point;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class SeeminglyUnmodifiable {
   private Map<String, Point> startingLocations = new HashMap<>(3);

   public SeeminglyUnmodifiable(){
      startingLocations.put("LeftRook", new Point(1, 1));
      startingLocations.put("LeftKnight", new Point(1, 2));
      startingLocations.put("LeftCamel", new Point(1, 3));
      //..more locations..
   }

   public Map<String, Point> getStartingLocations(){
      return Collections.unmodifiableMap(startingLocations);
   }

   public static void main(String [] args){
     SeeminglyUnmodifiable  pieceLocations = new SeeminglyUnmodifiable();
     Map<String, Point> locations = pieceLocations.getStartingLocations();

     Point camelLoc = locations.get("LeftCamel");
     System.out.println("The LeftCamel's start is at [ " + camelLoc.getX() +  ", " + camelLoc.getY() + " ]");

     //Try 1. update elicits Exception
     try{
        locations.put("LeftCamel", new Point(0,0));  
     } catch (java.lang.UnsupportedOperationException e){
        System.out.println("Try 1 - Could not update the map!");
     }

     //Try 2. Now let's try changing the contents of the object from the unmodifiable map!
     camelLoc.setLocation(0,0);

     //Now see whether we were able to update the actual map
     Point newCamelLoc = pieceLocations.getStartingLocations().get("LeftCamel");
     System.out.println("Try 2 - Map updated! The LeftCamel's start is now at [ " + newCamelLoc.getX() +  ", " + newCamelLoc.getY() + " ]");       }
}

When you run this example, you see:

The LeftCamel's start is at [ 1.0, 3.0 ]
Try 1 - Could not update the map!
Try 2 - Map updated! The LeftCamel's start is now at [ 0.0, 0.0 ]

The startingLocations map is encapsulated and only returned by leveraging Collections.unmodifiableMap in the getStartingLocations method. However, the scheme is subverted by getting access to any object and then changing it, as seen in "Try 2" in the code above. Suffice to say that one can only rely on Collections.unmodifiableMap to give a truly unmodifiable map IF the objects held in the map are themselves immutable. If they're not, we'd want to either copy the objects in the map or otherwise restrict access to the object's modifier methods, if possible.

Share:
32,669
Paulo Guedes
Author by

Paulo Guedes

I'm a Java programmer with some experience in C/C++, T-SQL, PL-SQL and Javascript.

Updated on September 16, 2022

Comments

  • Paulo Guedes
    Paulo Guedes almost 2 years

    I have a map of constants, like this:

    private static Map<String, Character> _typesMap =
            new HashMap<String, Character>() {
            {
                put ("string", 'S');
                put ("normalizedString", 'N');
                put ("token", 'T');
                // (...)
            }
    

    Do I really need to use Collections.unmodifiableMap() to create this map? What is the advantage of using it? Are there any disadvantages of not using it, besides the obvious fact that they are not really becoming constant?

  • Paulo Guedes
    Paulo Guedes over 13 years
    "It's much faster and more memory efficient than returning a copy of the map." -- that's what I wanted to know. :-)
  • Paulo Guedes
    Paulo Guedes over 13 years
    "it may not be so useful in production" -- why? That's my point, is it useful? is it really necessary?
  • Vishy
    Vishy over 13 years
    If you test it properly in a test environment, this should not be needed in production because you have checked that the collection is never modified.
  • Kelvin
    Kelvin almost 8 years
    Unless you have tested every code path in your test environment, you cannot be sure. If you miss a code path, it's usually better to fail fast (even in production) than to let the collection be modified and cause some hard-to-debug problem farther down the line.
  • Vishy
    Vishy almost 8 years
    @Kelvin either you want fail fast, in which case you want assertions on, or you want speed in which case you don't want to be creating objects which don't make any difference. You don't want something which is not really either.
  • User007
    User007 about 5 years
    in this example you modify the map ITEM, and not the map itself. the map is unmodifiable but its items not necessarily. the items of the collection are not the collection itself. they are just the aggregated contents. i think you should remove this "answer" as soon as possible!
  • z atef
    z atef about 2 years
    This is a valid answer.