16

I was just wondering if it is possible to define the contents of a Map Object on initialisation.

For example, an array can be created, as:

new String[] {“apples”, “bananas”, “pears”}

So, I was wondering if there is something similar we can do for maps.

Jonik
  • 80,077
  • 70
  • 264
  • 372
Larry
  • 11,439
  • 15
  • 61
  • 84
  • 2
    Closely related: http://stackoverflow.com/questions/507602/how-to-initialise-a-static-map-in-java?rq=1 – Jonik Jul 18 '13 at 11:32

4 Answers4

36

You can, sort of, using this syntax trick:

Map<String,String> map = new HashMap<String,String>() {{
    put("x", "y");
    put("a", "b");
}};

Not very pleasant, though. This creates an anonymous subclass of HashMap, and populates it in the instance initializer.

skaffman
  • 398,947
  • 96
  • 818
  • 769
  • Nice trick. I'd put those inner {}s on separate lines, though, as it looks very confusing right now. – Sergei Tachenov Jan 30 '11 at 13:21
  • @Sergey: Sure, whatever style you prefer. I've always used this style, though, and whenever I see this trick used, it also uses this style. You get used to it. The important thing is the initializer, that's what should be focussed on. – skaffman Jan 30 '11 at 13:23
  • Well, it's the first time I see this trick, I guess that explains confusion. – Sergei Tachenov Jan 30 '11 at 13:25
  • 2
    This is a really bad practice: it causes a reference to the current object to be leaked. I wish I could vote down but I cannot just yet. – Andrea Ratto Apr 15 '15 at 18:10
  • Thanks for the warning @AndreaRatto - to those wanting more info on why: it is because the new object `map` contains an implicit `this` object referencing the parent object. More info here: https://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/ – Dan Alvizu May 09 '17 at 18:04
  • The builder solution is better but @AndreaRatto, I hate myself for even suggesting it, but couldn't you immediately create a normal HashMap from the leaking version to release the leak? `new HashMap(new HashMap() {{put("x", "y"); put("a", "b");}});` – Ryan Oct 04 '17 at 19:32
16

If your Map is going to be immutable after creation and you don't mind adding a dependency, Guava offers some nice fluent syntax:

Map<K,V> aMap = ImmutableMap.<K,V>builder().put(key0, val0).put(key1,val1).build();

If you're feeling really exotic, Scala has syntax exactly like what you want and is interoperable with other Java code:

val aMap = Map("a"->0, "b"->1)

Note that the Scala compiler will infer the Map generic type is from String to Int, based on what you put in it, though you can explicitly specify it as well.

However, if this is just a one-off, I'd go with the initializer-based syntax. Both the Guava library and Scala language have a lot else to recommend them, but learning a whole new library/language might be overboard.

Carl
  • 7,538
  • 1
  • 40
  • 64
3

You can use initializer blocks:

class Foo {
   //using static initializer block 
   static Map<String,String> m1 = new HashMap<String,String>();
   static {
      m1.put("x","y");
      m1.put("a","b");
   }    

   //using initializer block 
   Map<String,String> m2 = new HashMap<String,String>();
   {
      m2.put("x","y");
      m2.put("a","b");
   }    

} 
Landei
  • 54,104
  • 13
  • 100
  • 195
  • Someone pointed out to me once that using instance initializers instead of constructors is a bad idea, particularly because it makes exception handling more difficult. static initializers are fine, though. – Sergei Tachenov Jan 30 '11 at 13:34
  • @Sergey: Depends. Read this: http://stackoverflow.com/questions/1355810/how-is-an-instance-initializer-different-from-a-constructor – Peter Knego Jan 30 '11 at 14:43
  • I wouldn't put error-prone code in initializers, but for safe operations like filling collections I see no problems. – Landei Jan 30 '11 at 16:51
1

Something very hacky..can be improved, but this is just a direction: Define a static helper to convert an object array to a map of this type:

    public static<K,V> Map<K, V> fromArray(Object[] anObjArray){
    int size = anObjArray.length;
    Map<K, V> aMap = new HashMap<K, V>();
    for (int i=0;i<=size/2;i=i+2){
        K key = (K)anObjArray[i];
        V value = (V)anObjArray[i+1];
        aMap.put(key, value);
    }
    return aMap;
}

then you can create a map using this:

        Map<Integer, String> aMap = MapUtils.<Integer, String>fromArray(new Object[]{1, "one", 2,"two"});

I would personally second Gauva builder suggestion from @Carl though :-)

Biju Kunjummen
  • 49,138
  • 14
  • 112
  • 125