views:

20165

answers:

12

How would you initialise a static Map in Java?

Method one: Static initialiser Method two: instance initialiser (anonymous subclass) or some other method?

What are the pros and cons of each?

Here is an example illustrating two methods:

import java.util.HashMap;
import java.util.Map;

public class Test {
    private static final Map<Integer, String> myMap = new HashMap<Integer, String>();
    static {
        myMap.put(1, "one");
        myMap.put(2, "two");
    }

    private static final Map<Integer, String> myMap2 = new HashMap<Integer, String>(){
        {
            put(1, "one");
            put(2, "two");
        }
    };
}
+10  A: 

One advantage to the second method is that you can wrap it with Collections.unmodifiableMap() to guarantee that nothing is going to update the collection later:

private static final Map<Integer, String> CONSTANT_MAP = 
    Collections.unmodifiableMap(new HashMap<Integer, String>() {{ 
        put(1, "one");
        put(2, "two");
    }});

 // later on...

 CONSTANT_MAP.put(3, "three"); // going to throw an exception!
Outlaw Programmer
Can't you easily do this in the first method by moving the new operator into the static {} block and wrapping it?
Patrick
I'd move the constructor call into the static initialised anyway. Anything else just looks odd.
Tom Hawtin - tackline
any idea what performance hit there might be from using an anonymous class as opposed to a concrete class?
Kip
A: 

The second method could invoke protected methods if needed. This can be useful for initializing classes which are immutable after construction.

Mark Renouf
+26  A: 

The instance initialiser is just syntactic sugar in this case, right? I don't see why you need an extra anonymous class just to initialize. And it won't work if the class being created is final.

You can create an immutable map using a static initialiser too:

public class Test {
    private static final Map<Integer, String> myMap;
    static {
        Map<Integer, String> aMap = ....;
        aMap.put(1, "one");
        aMap.put(2, "two");
        myMap = Collections.unmodifiableMap(aMap);
    }
}
Hemal Pandya
This is the idiom I've used for years and I've never had anyone bat an eye at it. I do the same for unmodifiable constant Sets and Lists too.
jasonmp85
+6  A: 

I would never create an anonymous subclass in this situation. Static initializers work equally well, if you would like to make the map unmodifiable for example:

private static final Map<Integer, String> MY_MAP;
static
{
 Map<Integer, String>tempMap = new HashMap<Integer, String>();
 tempMap.put(1, "one");
 tempMap.put(2, "two");
 MY_MAP = Collections.unmodifiableMap(tempMap);
}
eljenso
In which situation would you use an anonymous subclass to initialise a hashmap then?
dogbane
Never to initialize a Collection.
eljenso
A: 

I like the anonymous class syntax; it's just less code. However, one major con I have found is that you won't be able to serialize that object via remoting. You will get an exception about not being able to find the anonymous class on the remote side.

Chase Seibert
You could create the map using the double-brace idiom, and then copy it.
Tom Hawtin - tackline
+2  A: 

The anonymous class you're creating works well. However you should be aware that this is an inner class and as such, it'll contain a reference to the surrounding class instance. So you'll find you can't do certain things with it (using XStream for one). You'll get some very strange errors.

Having said that, so long as you're aware then this approach is fine. I use it most of the time for initialising all sorts of collections in a concise fashion.

EDIT: Pointed out correctly in the comments that this is a static class. Obviously I didn't read this closely enough. However my comments do still apply to anonymous inner classes.

Brian Agnew
In this particular case it's static, so no outer instance.
Tom Hawtin - tackline
Arguably XStream shouldn't be trying to serialize stuff like this (it's static. Why would you need to serialize a static variable?)
jasonmp85
See Tom's comment above and my edit.
Brian Agnew
+1  A: 

I've done something a bit different. Not the best, but it works for me. Maybe it could be "genericized".

private static final Object[][] ENTRIES =
{
  {new Integer(1), "one"},
  {new Integer(2), "two"},
};
private static final Map myMap = newMap(ENTRIES);

private static Map newMap(Object[][] entries)
{
  Map map = new HashMap();

  for (int x = 0; x < entries.length; x++)
  {
    Object[] entry = entries[x];

    map.put(entry[0], entry[1]);
  }

  return map;
}
Gary Kephart
+3  A: 

Maybe it's interesting to check out Google Collections, e.g. the videos that they have on their page. They provide various ways to initialize maps and sets, and provide immutable collections as well.

Kaarel
I love Google collections.
Hemal Pandya
+11  A: 

I would use:

public class Test {
    private static final Map<Integer, String> MY_MAP = createMap();

    private static Map<Integer, String> createMap() {
        Map<Integer, String> result = new HashMap<Integer, String>();
        result.put(1, "one");
        result.put(2, "two");
        return Collections.unmodifiableMap(result);
    }
}
  1. it avoids anonymous class, which I personally consider to be a bad style, and avoid
  2. it makes creation of map more explicit
  3. it makes map unmodifiable
  4. as MY_MAP is constant, I would name it like constant
Peter Štibraný
+1 using a method seems so obvious now; I wish I had mentioned it in my answer as well.
eljenso
+2  A: 

Java 5 provides this more compact syntax:

static final Map<String , String> FLAVORS = new HashMap<String , String>() {{
    put("Up",    "Down");
    put("Charm", "Strange");
    put("Top",   "Bottom");
}};
Chris Noe
That technique is called double brace initialization: http://stackoverflow.com/questions/1372113/meaning-of-new-class-initialization-idiom/1372124#1372124 It's not a special Java 5 syntax, it's just a trick with an anonymous class with an instance initializer.
Jesper
A: 

Assuming this is a statically initialized (or at least infrequently initialized) collection, the following code is a compact way of doing this in a single expression without creating any new inner/anonymous classes, new methods, or having to write a whole initialization block:

  static final Set<String> providedNames = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
  "Fuel", "Water")));
Christer
A: 
public class Test {
    private static final Map<Integer, String> myMap;
    static {
        Map<Integer, String> aMap = ....;
        aMap.put(1, "one");
        aMap.put(2, "two");
        myMap = Collections.unmodifiableMap(aMap);
    }
}

If we declare more than one constant then that code will be written in static block and that is hard to maintain in future. So it is better to use anonymous class.

public class Test {

    public static final Map numbers = Collections.unmodifiableMap(new HashMap(2, 1.0f){
        {
            put(1, "one");
            put(2, "two");
        }
    });
}

And it is suggested to used unmodifiableMap for constants other wise it can't be treated as constant.

Leninkumar Koppoju