Inline initialization of Java Maps

Inline initialization of Java Maps is never pretty, this solution provides a type safe and efficient way to do so..

Neil Buesing

Inline initialization of maps in Java has been a sign of weakness in the Java language. This approach has given me the most concise way to initialize maps, while meeting all of my goals.

Goals:

  1. minimal syntax
  2. no anonymous inner classes
  3. support different Key/Value types
  4. visually show the key=value pairing
  5. mimic Arrays.asList() syntax
  6. support unmodifiable
  7. support any Map implementation (in my case, HashMap and LinkedHashMap)

I have found articles and 3rd part libraries that come close, and my be the idea choice for you. However, none met all of the goals above, so I kept looking for a new solution (I know, goals like ‘minimal syntax’ is up for debate, if I actually achieved that).

If you include MapUtils, which is less than 50 lines of code, and you statically import one of the methods, MapUtils.entry() you can initialize a Map as follows:

Map<String,Integer> map = MapUtils.asMap(entry(“A”, 1), entry(“B”, 2), entry(“C”, 3), entry(“D”, 4)); Map<String,Integer> map = MapUtils.asUnmodifiableMap(entry(“A”, 1), entry(“B”, 2), entry(“C”, 3), entry(“D”, 4));

Here is the source code for MapUtils.java

package com.objectpartners.buesing.util;

import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @author Neil Buesing
 */
public final class MapUtils {

  public static class Entry<K, V> {
    private K key;
    private V value;

    public Entry(final K key, final V value) {
      this.key = key;
      this.value = value;
    }

    public K getKey() {
      return key;
    }

    public V getValue() {
      return value;
    }
  }

  public static <K, V> Entry<K, V> entry(final K key, final V value) {
    return new Entry(key, value);
  }

  public static <K, V> Map<K, V> asMap(final Entry<K, V>... entries) {
    return populate(new HashMap<K, V>(), entries);
  }

  public static <K, V> Map<K, V> asOrderedMap(final Entry<K, V>... entries) {
    return populate(new LinkedHashMap<K, V>(), entries);
  }

  public static <K, V> Map<K, V> asUnmodifiableMap(final Entry<K, V>... entries) {
    return Collections.unmodifiableMap(populate(new HashMap<K, V>(), entries));
  }

  public static <K, V> Map<K, V> asUnmodifiableOrderedMap(final Entry<K, V>... entries) {
    return Collections.unmodifiableMap(populate(new LinkedHashMap<K, V>(), entries));
  }

  private static <K, V> Map<K, V> populate(final Map map, final Entry<K, V>... entries) {
    for (final Entry entry : entries) {
      map.put(entry.getKey(), entry.getValue());
    }
    return map;
  }
}

This is a simple unit test showing various ways to initialize inline maps; compare and see what technique you like best.

package com.objectpartners.buesing.util;

import com.google.common.collect.ImmutableMap;
import org.junit.Assert;
import org.junit.Test;

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

import static com.objectpartners.buesing.util.MapUtils.entry;

/**
 * @author Neil Buesing
 */
public class MapInitializationTest {

  //the approach described in this blog
  private Map<String, Integer> mapA = MapUtils.asMap(entry("A", 1), entry("B", 2));

  //the approach described in this blog (unmodifiable)
  private Map<String, Integer> mapB = MapUtils.asUnmodifiableMap(entry("A", 1), entry("B", 2));

  //block initialization
  private Map<String, Integer> mapC = new HashMap<String, Integer>();
  {
    mapA.put("A", 1);
    mapA.put("B", 2);
  }

  //block initialization / unmodifiable
  private Map<String, Integer> mapD;
  {
    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("A", 1);
    map.put("B", 2);
    mapD = Collections.unmodifiableMap(map);
  }

  //inheritance with block initialization
  private Map<String, Integer> mapE = new HashMap<String, Integer>() {{
    put("A", 1);
    put("B", 2);
  }};

  //3rd party library Guava
  private Map mapF = ImmutableMap.builder().put("A", 1).put("B", 2).build();

  @Test
  public void test() {
    Assert.assertEquals(mapA, mapB);
    Assert.assertEquals(mapA, mapC);
    Assert.assertEquals(mapA, mapD);
    Assert.assertEquals(mapA, mapE);
    Assert.assertEquals(mapA, mapF);
  }

}

For completeness sake, here is a unit test MapUtilsTest.java

package com.objectpartners.buesing.util;

import org.junit.Assert;
import org.junit.Test;

import javax.naming.OperationNotSupportedException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Map;

import static com.objectpartners.buesing.util.MapUtils.entry;

/**
 * @author Neil Buesing
 */
public class MapUtilsTest {

    @Test
    public void testAsMap() {
        Map<String, Integer> map = MapUtils.asMap(entry("A", 1), entry("B", 2));
        Assert.assertEquals(2, map.size());
        Assert.assertEquals(Integer.valueOf(1), map.get("A"));
        Assert.assertEquals(Integer.valueOf(2), map.get("B"));
    }

    @Test
    public void testAsOrderedMap() {
        Map<String, Integer> map = MapUtils.asOrderedMap(
                entry("Z", 1), entry("Y", 9), entry("X", 2), entry("W", 7), entry("C", 3), entry("B", 6), entry("A", 4)
        );
        Assert.assertEquals(7, map.size());
        Assert.assertEquals(Arrays.asList("Z", "Y", "X", "W", "C", "B", "A"), new ArrayList<String>(map.keySet()));
        Assert.assertEquals(Arrays.asList(1, 9, 2, 7, 3, 6, 4), new ArrayList<Integer>(map.values()));
    }

    @Test
    public void testAsUnmodifiableMap() {
        Map<String, Integer> map = MapUtils.asUnmodifiableMap(entry("A", 1), entry("B", 2));
        Assert.assertEquals(2, map.size());
        Assert.assertEquals(Integer.valueOf(1), map.get("A"));
        Assert.assertEquals(Integer.valueOf(2), map.get("B"));

        try {
            map.put("AA", 33);
            Assert.fail("failed to throw exception");
        } catch (UnsupportedOperationException e) {
            // expected
        }
    }

    @Test
    public void testAsUnmodifiableOrderedMap() {
        Map<String, Integer> map = MapUtils.asUnmodifiableOrderedMap(
                entry("Z", 1), entry("Y", 9), entry("X", 2), entry("W", 7), entry("C", 3), entry("B", 6), entry("A", 4)
        );
        Assert.assertEquals(7, map.size());
        Assert.assertEquals(Arrays.asList("Z", "Y", "X", "W", "C", "B", "A"), new ArrayList<String>(map.keySet()));
        Assert.assertEquals(Arrays.asList(1, 9, 2, 7, 3, 6, 4), new ArrayList<Integer>(map.values()));

        try {
            map.put("AA", 33);
            Assert.fail("failed to throw exception");
        } catch (UnsupportedOperationException e) {
            // expected
        }
    }
}

Now, maybe someday, Java will learn from Groovy and add these higher level data structure concepts to the language. While I can appreciate a language that tries not to embedded higher-level object concepts into the core, Java has already crossed this line with the introduction of Java5 for-loop syntax. Also, from Java’s beginning, making String objects from character arrays. In the meantime we all search the web and find various techniques to try to reduce the amount of Java code we write. Maybe someday I can work on an application server that allows me to utilize Java 8 and I can finally have my lambda expressions; or at least Java’s implementation and I can start getting rid of more of the boiler plate code I’m forced to write. For now, I will just be happy with my inlined initialized Maps.

Share this Post

Related Blog Posts

JVM

Run Grails Commands on Heroku

May 22nd, 2014

Instructions on how to run Grails commands on Heroku using an example of how to run Grails migrations on Heroku

Brandon Fish
JVM

Configuring Spring Security CAS Providers with Java Config

May 20th, 2014

It can take a little digging to move Spring Security + CAS XML configuration to Java. Here is the Java config equivalent to the SS documentations example.

Object Partners
JVM

Automatically test your dirty Grails classes

May 15th, 2014

A script to grab the list of modified files from git, parse their filenames, and pass those names for testing in grails test-app.

Igor Shults

About the author

Neil Buesing

Sr. Consultant

Neil has more than twenty-two years of Object oriented development experience, with fifteen years of experience with Java/J2EE application development. He has successfully delivered in the role of architect and as lead developer.