How to directly initialize a HashMap (in a literal way)?

Viewed 1658339

Is there some way of initializing a Java HashMap like this?:

Map<String,String> test = 
    new HashMap<String, String>{"test":"test","test":"test"};

What would be the correct syntax? I have not found anything regarding this. Is this possible? I am looking for the shortest/fastest way to put some "final/static" values in a map that never change and are known in advance when creating the Map.

22 Answers

JAVA 8

In plain java 8 you also have the possibility of using Streams/Collectors to do the job.

Map<String, String> myMap = Stream.of(
         new SimpleEntry<>("key1", "value1"),
         new SimpleEntry<>("key2", "value2"),
         new SimpleEntry<>("key3", "value3"))
        .collect(toMap(SimpleEntry::getKey, SimpleEntry::getValue));

This has the advantage of not creating an Anonymous class.

Note that the imports are:

import static java.util.stream.Collectors.toMap;
import java.util.AbstractMap.SimpleEntry;

Of course, as noted in other answers, in java 9 onwards you have simpler ways of doing the same.

tl;dr

Use Map.of… methods in Java 9 and later.

Map< String , String > animalSounds =
    Map.of(
        "dog"  , "bark" ,   // key , value
        "cat"  , "meow" ,   // key , value
        "bird" , "chirp"    // key , value
    )
;

Map.of

Java 9 added a series of Map.of static methods to do just what you want: Instantiate an immutable Map using literal syntax.

The map (a collection of entries) is immutable, so you cannot add or remove entries after instantiating. Also, the key and the value of each entry is immutable, cannot be changed. See the Javadoc for other rules, such as no NULLs allowed, no duplicate keys allowed, and the iteration order of mappings is arbitrary.

Let's look at these methods, using some sample data for a map of day-of-week to a person who we expect will work on that day.

Person alice = new Person( "Alice" );
Person bob = new Person( "Bob" );
Person carol = new Person( "Carol" );

Map.of()

Map.of creates an empty Map. Unmodifiable, so you cannot add entries. Here is an example of such a map, empty with no entries.

Map < DayOfWeek, Person > dailyWorkerEmpty = Map.of();

dailyWorkerEmpty.toString(): {}

Map.of( … )

Map.of( k , v , k , v , …) are several methods that take 1 to 10 key-value pairs. Here is an example of two entries.

Map < DayOfWeek, Person > weekendWorker = 
        Map.of( 
            DayOfWeek.SATURDAY , alice ,     // key , value
            DayOfWeek.SUNDAY , bob           // key , value
        )
;

weekendWorker.toString(): {SUNDAY=Person{ name='Bob' }, SATURDAY=Person{ name='Alice' }}

Map.ofEntries( … )

Map.ofEntries( Map.Entry , … ) takes any number of objects implementing the Map.Entry interface. Java bundles two classes implementing that interface, one mutable, the other immutable: AbstractMap.SimpleEntry, AbstractMap.SimpleImmutableEntry. But we need not specify a concrete class. We merely need to call Map.entry( k , v ) method, pass our key and our value, and we get back an object of a some class implementing Map.Entry interface.

Map < DayOfWeek, Person > weekdayWorker = Map.ofEntries(
        Map.entry( DayOfWeek.MONDAY , alice ) ,            // Call to `Map.entry` method returns an object implementing `Map.Entry`. 
        Map.entry( DayOfWeek.TUESDAY , bob ) ,
        Map.entry( DayOfWeek.WEDNESDAY , bob ) ,
        Map.entry( DayOfWeek.THURSDAY , carol ) ,
        Map.entry( DayOfWeek.FRIDAY , carol )
);

weekdayWorker.toString(): {WEDNESDAY=Person{ name='Bob' }, TUESDAY=Person{ name='Bob' }, THURSDAY=Person{ name='Carol' }, FRIDAY=Person{ name='Carol' }, MONDAY=Person{ name='Alice' }}

Map.copyOf

Java 10 added the method Map.copyOf. Pass an existing map, get back an immutable copy of that map.

Notes

Notice that the iterator order of maps produced via Map.of are not guaranteed. The entries have an arbitrary order. Do not write code based on the order seen, as the documentation warns the order is subject to change.

Note that all of these Map.of… methods return a Map of an unspecified class. The underlying concrete class may even vary from one version of Java to another. This anonymity enables Java to choose from various implementations, whatever optimally fits your particular data. For example, if your keys come from an enum, Java might use an EnumMap under the covers.

I would like to give a brief warning to Johnny Willer's answer.

Collectors.toMap relies on Map.merge and does not expect null values, so it will throw a NullPointerException as it was described in this bug report: https://bugs.openjdk.java.net/browse/JDK-8148463

Also, if a key appears several times, the default Collectors.toMap will throw an IllegalStateException.

An alternative way to get a map with null values using a builder syntax on Java 8 is writing a custom collector backed by a HashMap (because it does allow null values):

Map<String, String> myMap = Stream.of(
         new SimpleEntry<>("key1", "value1"),
         new SimpleEntry<>("key2", (String) null),
         new SimpleEntry<>("key3", "value3"),
         new SimpleEntry<>("key1", "value1updated"))
        .collect(HashMap::new,
                (map, entry) -> map.put(entry.getKey(),
                                        entry.getValue()),
                HashMap::putAll);

We can use AbstractMap Class having SimpleEntry which allows the creation of immutable map

Map<String, String> map5 = Stream.of(
    new AbstractMap.SimpleEntry<>("Sakshi","java"),
    new AbstractMap.SimpleEntry<>("fine","python")
    ).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    
    System.out.println(map5.get("Sakshi"));
    map5.put("Shiva", "Javascript");
    System.out.println(map5.get("Shiva"));// here we can add multiple entries.

We use a simple utility class to initialize Maps in a fluent way:

Map<String, String> map = MapInit
    .init("key1", "value1")
    .put("key2", "value2")
    .put("key3", "value3")
    .getMap();

The utility class isn't limited neither regarding the type of keys and values nor regarding the amount of entries nor regarding the type of the resulting Map.

The utility class looks like the following:

public class MapInit<K, V, T extends Map<K, V>> {
    private final T map;


    private MapInit(final T map) {
        this.map = map;
    }

    public T getMap() {
        return this.map;
    }

    public static <K, V> MapInit<K, V, HashMap<K, V>> init(final K key, final V value) {
        return init(HashMap::new, key, value);
    }

    public static <K, V, T extends Map<K, V>> MapInit<K, V, T> init(final Supplier<T> mapSupplier, final K key, final V value) {
        return new MapInit<>(mapSupplier.get()) //
                .put(key, value);
    }

    public MapInit<K, V, T> put(final K key, final V value) {
        this.map.put(key, value);
        return this;
    }
}

You could possibly make your own Map.of (which is only available in Java 9 and higher) method easily in 2 easy ways

Make it with a set amount of parameters

Example

public <K,V> Map<K,V> mapOf(K k1, V v1, K k2, V v2 /* perhaps more parameters */) {
    return new HashMap<K, V>() {{
      put(k1, v1);
      put(k2,  v2);
      // etc...
    }};
}

Make it using a List

You can also make this using a list, instead of making a lot of methods for a certain set of parameters.

Example

public <K, V> Map<K, V> mapOf(List<K> keys, List<V> values) {
   if(keys.size() != values.size()) {
        throw new IndexOutOfBoundsException("amount of keys and values is not equal");
    }

    return new HashMap<K, V>() {{
        IntStream.range(0, keys.size()).forEach(index -> put(keys.get(index), values.get(index)));
    }};
}

Note It is not recommended to use this for everything as this makes an anonymous class every time you use this.

If you need to place only one key-value pair, you can use Collections.singletonMap(key, value);

With Java 8 or less

You can use static block to initialize a map with some values. Example :

public static Map<String,String> test = new HashMap<String, String>

static {
    test.put("test","test");
    test.put("test1","test");
}

With Java 9 or more

You can use Map.of() method to initialize a map with some values while declaring. Example :

public static Map<String,String> test = Map.of("test","test","test1","test");

Simple way to do this:

public static Map<String, String> mapWithValues(String...values) {
    Map<String, String> map = new HashMap<String, String>();
    
    for (int x = 0; x < values.length; x = x+2) {
        map.put(values[x], values[x+1]);
    }
    
    return map;
}

If it's an instance variable, then an instance initialization block is definitely the way to go, especially if you can't use Map.of() because you need a different type of map.

But if you're feeling frisky, you could use a Java 8 Supplier (not recommended).

private final Map<String,Runnable> games = ((Supplier<Map<String,Runnable>>)() -> {
  Map<String,Runnable> map = new LinkedHashMap<>();

  map.put("solarus",this::playSolarus);
  map.put("lichess",this::playLichess);

  return map;
}).get();

Or make your own functional interface (looks fine to me):

@FunctionalInterface
public interface MapMaker<M> {
  static <M extends Map<K,V>,K,V> M make(M map,MapMaker<M> maker) {
    maker.build(map);
    return map;
  }

  void build(M map);
}

// Can use LinkedHashMap!
private final Map<String,Runnable> games = MapMaker.make(
    new LinkedHashMap<>(),(map) -> {
      map.put("solarus",this::playSolarus);
      map.put("lichess",this::playLichess);
    });

Unfortunately, using varargs if the type of the keys and values are not the same is not very reasonable as you'd have to use Object... and lose type safety completely. If you always want to create e.g. a Map<String, String>, of course a toMap(String... args) would be possible though, but not very pretty as it would be easy to mix up keys and values, and an odd number of arguments would be invalid.

You could create a sub-class of HashMap that has a chainable method like

public class ChainableMap<K, V> extends HashMap<K, V> {
  public ChainableMap<K, V> set(K k, V v) {
    put(k, v);
    return this;
  }
}

and use it like new ChainableMap<String, Object>().set("a", 1).set("b", "foo")

Another approach is to use the common builder pattern:

public class MapBuilder<K, V> {
  private Map<K, V> mMap = new HashMap<>();

  public MapBuilder<K, V> put(K k, V v) {
    mMap.put(k, v);
    return this;
  }

  public Map<K, V> build() {
    return mMap;
  }
}

and use it like new MapBuilder<String, Object>().put("a", 1).put("b", "foo").build();

However, the solution I've used now and then utilizes varargs and the Pair class:

public class Maps {
  public static <K, V> Map<K, V> of(Pair<K, V>... pairs) {
    Map<K, V> = new HashMap<>();

    for (Pair<K, V> pair : pairs) {
      map.put(pair.first, pair.second);
    }

    return map;
  }
}

Map<String, Object> map = Maps.of(Pair.create("a", 1), Pair.create("b", "foo");

The verbosity of Pair.create() bothers me a bit, but this works quite fine. If you don't mind static imports you could of course create a helper:

public <K, V> Pair<K, V> p(K k, V v) {
  return Pair.create(k, v);
}

Map<String, Object> map = Maps.of(p("a", 1), p("b", "foo");

(Instead of Pair one could imagine using Map.Entry, but since it's an interface it requires an implementing class and/or a helper factory method. It's also not immutable, and contains other logic not useful for this task.)

You can use Streams In Java 8 (this is exmaple of Set):

@Test
public void whenInitializeUnmodifiableSetWithDoubleBrace_containsElements() {
    Set<String> countries = Stream.of("India", "USSR", "USA")
      .collect(collectingAndThen(toSet(), Collections::unmodifiableSet));

    assertTrue(countries.contains("India"));
}

Ref: https://www.baeldung.com/java-double-brace-initialization

You can create a method to initialize the map like in this example below:

Map<String, Integer> initializeMap()
{
  Map<String, Integer> ret = new HashMap<>();

  //populate ret
  ...

  return ret;
}

//call
Map<String, Integer> map = initializeMap();

Following code can do the trick in Java 8:

Map<String, Integer> map = Stream.of(new Object[][] { 
     { "data1", 1 }, 
     { "data2", 2 }, 
 }).collect(Collectors.toMap(data -> (String) data[0], data -> (Integer) data[1]));

credits:

Map.of() seems most universal and least limited. Here, it takes care of non object input values automaticaly :

List<Map<String, Object> certs = new ArrayList<>(){{ add( Map.of(
    "TAG",          Obj1 // Object
    "TAGGED_ID",    1L //Long
    "DEGREE",       "PARENT" // String
    "DATE",         LocalDate.now() // LocalDate
));}};

Note that maps created by static Map.of(..) constuctor don't allow neither keys nor values to be null .

I found a great article by baeldung that lists several ways to do this in different Java versions.

A couple of interesting ways that can be handy are

For any Java version

public static Map<String, String> articleMapOne;
static {
    articleMapOne = new HashMap<>();
    articleMapOne.put("ar01", "Intro to Map");
    articleMapOne.put("ar02", "Some article");
}

For Java 8 using streams

Map<String, String> map = Stream.of(new String[][] {
  { "Hello", "World" }, 
  { "John", "Doe" }, 
}).collect(Collectors.toMap(data -> data[0], data -> data[1]));
Related