11
\$\begingroup\$

I am always tired of the very verbose syntax in Java for creating small maps. Arrays.asList(T... a) is very convenient, and therefore I want something similar for a map.

public class MapUtils {
    public static <K, V> MapBuilder<K, V> asMap(K key, V value) {
        return new MapBuilder<K, V>().entry(key, value);
    }

    public static class MapBuilder<K, V> extends HashMap<K, V> {
        public MapBuilder<K, V> entry(K key, V value) {
            this.put(key, value);
            return this;
        }
    }
}

/* Example of how to use asMap */
public class Example {
    public void example() {
        Map<String, String> map = MapUtil.asMap("key", "value").entry("key2", "value2");
    }
}

/* Example of how the one way to create an inline Map in Java */
public class Before {
    public void before() {
        Map<String, String> map = new HashMap<String, String>() {{
            put("key", value");
            put("key2", value2");
        }};
    }
} 

Any inputs on how to improve this implementation of MapUtils? Do you consider the asMap function a good idea, or am I too lazy to write some extra characters?

\$\endgroup\$
  • \$\begingroup\$ Looks nice to me. But Landeis solution seems reasonable. :) \$\endgroup\$ – user unknown Jul 1 '11 at 21:33
8
\$\begingroup\$

In my opinion MapBuilder should contain a Map, not extend one. Another possible design would be to use a single call with varargs:

public class MapUtil {
    public static <K,V> Map<K,V> asMap(Map.Entry<K,V>... entries) {
        Map<K,V> map = new HashMap<K,V>();
        for(Map.Entry<K,V> entry : entries) {
            map.put(entry.getKey(), entry.getValue());
        }
        return map;
    }

    public static <K,V> Map.Entry<K,V> e(final K k, final V v) {
        return new Map.Entry<K, V>() {
            public K getKey() {
                return k;
            }
            public V getValue() {
                return v;
            }
            public V setValue(V value) {
                throw new UnsupportedOperationException("Not supported");
            }
        };
    }
} 

import static very.useful.MapUtil.*;
... 
Map<String, Integer> map = asMap(e("x",1),e("y",2),e("z",3));
\$\endgroup\$
  • 1
    \$\begingroup\$ Well, well, but e? Just e? A bit aggressive and cryptic. \$\endgroup\$ – user unknown Jul 1 '11 at 21:35
  • \$\begingroup\$ I used e for "entry", but of course this is a matter of taste. The best thing would be an operator as in Scala (here ->), but the only symbolic things we have for Java names are $ and _. Hmmm, maybe we should use $$ for pairs, that would make at least a little bit sense. \$\endgroup\$ – Landei Jul 3 '11 at 8:33
  • 3
    \$\begingroup\$ Well, or maybe entry for entry? :) \$\endgroup\$ – user unknown Jul 3 '11 at 14:36
  • 1
    \$\begingroup\$ @user unknown: IMHO that would be too long. \$\endgroup\$ – Landei Jul 3 '11 at 18:07
  • \$\begingroup\$ I don't like the e-function either, but I guess it is a necessary evil. \$\endgroup\$ – smat Jul 4 '11 at 12:29
5
\$\begingroup\$

google-collections has all about it, e.g. ImmutableMap.of(). Moreover they teach using right data structure, like true immutable collections, ordered lists etc. I really enjoyed it.


Google Collections is now part of Google Guava, and it includes ImmutableMap.Builder. With this you can do:

   static final ImmutableMap<String, Integer> WORD_TO_INT =
   new ImmutableMap.Builder<String, Integer>()
       .put("one", 1)
       .put("two", 2)
       .put("three", 3)
       .build();

You can call the put(K, V) method as many times as you like. Builders are great for this kind of situation.

\$\endgroup\$
  • \$\begingroup\$ Nice tip with google-collections. But it only supports up to five entries. \$\endgroup\$ – smat Jul 4 '11 at 12:30
  • \$\begingroup\$ I have edited this answer with an example of using ImmutableMap.Builder, which gets around the 5 entries limit. I'm waiting for it to be peer reviewed. \$\endgroup\$ – Rich Jul 6 '11 at 11:26
1
\$\begingroup\$

I like Landei version, but I have also an untyped version of asMap like asMap(key,value,key,value,...). It is not typed Map, but in many cases, it fits the bill pretty well.

So

Map quickMap = asMap("prop1","val1","prop2",12,"prop3");

In this case "prop3" will have a value null.

Implementation:

static final public Map<String, Object> asMap(Object... objs) {
    HashMap<String, Object> m = new HashMap<String, Object>();

    for (int i = 0; i < objs.length; i += 2) {
        String key = objs[i].toString();
        if (i + 1 < objs.length) {
            Object value = objs[i + 1];
            m.put(key, value);
        } else {
            m.put(key, null);
        }
    }
    return m;
}
\$\endgroup\$
  • \$\begingroup\$ I wouldn't recommend this. Only the last value can contain null this way. \$\endgroup\$ – Yuri Sep 16 '15 at 10:16
  • \$\begingroup\$ @yuri I am not sure, because a asMap("prop1",null,"prop2",12,"prop3"); should work, no? \$\endgroup\$ – Jeremy Chone Sep 17 '15 at 1:16
  • \$\begingroup\$ sure, but it is inconsistent, which means that if you get an odd list of objects, chances are the programmer made a mistake. So I would at least implement a check on %2==0 for the argument list. And you always return something of type Map<String, Object>, maybe make a templated version? \$\endgroup\$ – Yuri Sep 18 '15 at 5:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.