Initialize a HashMap in Java

1. Overview

In this tutorial, we’ll learn about various ways of initializing a HashMap in Java.

We’ll use Java 8 as well as Java 9.

2. The Static Initializer for a Static HashMap

We can initialize a HashMap using a static block of code:

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

The advantage of this kind of initialization is that the map is mutable, but it will only work for static. Consequently, entries can be added and removed as and when required.

Let’s go ahead and test it:

@Test
public void givenStaticMap_whenUpdated_thenCorrect() {
    
    MapInitializer.articleMapOne.put(
      "NewArticle1", "Convert array to List");
    
    assertEquals(
      MapInitializer.articleMapOne.get("NewArticle1"), 
      "Convert array to List");  
}

We can also initialize the map using the double-brace syntax:

Map<String, String> doubleBraceMap  = new HashMap<String, String>() {{
    put("key1", "value1");
    put("key2", "value2");
}};

Note that we must try to avoid this initialization technique because it creates an anonymous extra class at every usage, holds hidden references to the enclosing object, and might cause memory leak issues.

3. Using Java Collections

If we need to create a singleton immutable map with a single entry, Collections.singletonMap() becomes very useful:

public static Map<String, String> createSingletonMap() {
    return Collections.singletonMap("username1", "password1");
}

Note that the map here is immutable, and if we try to add more entries, it’ll throw java.lang.UnsupportedOperationException.

We can also create an immutable empty map by using Collections.emptyMap():

Map<String, String> emptyMap = Collections.emptyMap();

4. The Java 8 Way

In this section, let’s look into the ways to initialize a map using Java 8 Stream API.

4.1. Using Collectors.toMap()

Let’s use a Stream of a two-dimensional String array and collect them into a map:

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

Notice here the data type of key and value of the Map is the same.

In order to make it more generic, let’s take the array of Objects and perform the same operation:

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

As a result, we create a map of the key as a String and value as an Integer.

4.2. Using a Stream of Map.Entry

Here we’ll use the instances of Map.Entry. This is another approach where we have different key and value types.

First, let’s use SimpleEntry implementation of the Entry interface:

Map<String, Integer> map = Stream.of(
  new AbstractMap.SimpleEntry<>("idea", 1), 
  new AbstractMap.SimpleEntry<>("mobile", 2))
  .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

Now let’s create the map using SimpleImmutableEntry implementation:

Map<String, Integer> map = Stream.of(
  new AbstractMap.SimpleImmutableEntry<>("idea", 1),    
  new AbstractMap.SimpleImmutableEntry<>("mobile", 2))
  .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

4.3. Initializing an Immutable Map

In certain use cases, we need to initialize an immutable map. This could be done by wrapping the Collectors.toMap() inside Collectors.collectingAndThen():

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

Note that we should avoid using such initialization using Streams, as it could cause a huge performance overhead and lots of garbage objects are created just to initialize the map.

5. The Java 9 Way

Java 9 comes with various factory methods in the Map interface that simplify the creation and initialization of immutable maps.

Let’s go ahead and look into these factory methods.

5.1. Map.of()

This factory method takes no argument, a single argument, and variable arguments:

Map<String, String> emptyMap = Map.of();
Map<String, String> singletonMap = Map.of("key1", "value");
Map<String, String> map = Map.of("key1","value1", "key2", "value2");

Note that this method supports only a maximum of 10 key-value pairs.

5.2. Map.ofEntries()

It’s similar to the Map.of() but has no limitations on the number of key-value pairs:

Map<String, String> map = Map.ofEntries(
  new AbstractMap.SimpleEntry<String, String>("name", "John"),
  new AbstractMap.SimpleEntry<String, String>("city", "budapest"),
  new AbstractMap.SimpleEntry<String, String>("zip", "000000"),
  new AbstractMap.SimpleEntry<String, String>("home", "1231231231")
);

Note that the factory methods produce immutable maps, hence any mutation will result in a UnsupportedOperationException.

Also, they do not allow null keys or duplicate keys.

Now if we need a mutable or growing map after initialization, we can create any of the implementations of the Map interface and pass these immutable maps in the constructor:

Map<String, String> map = new HashMap<String, String> (
  Map.of("key1","value1", "key2", "value2"));
Map<String, String> map2 = new HashMap<String, String> (
  Map.ofEntries(
    new AbstractMap.SimpleEntry<String, String>("name", "John"),    
    new AbstractMap.SimpleEntry<String, String>("city", "budapest")));

6. Using Guava

As we’ve looked into the ways of using core Java, let’s move ahead and initialize a map using the Guava library:

Map<String, String> articles 
  = ImmutableMap.of("Title", "My New Article", "Title2", "Second Article");

This would create an immutable map, and to create a mutable one:

Map<String, String> articles 
  = Maps.newHashMap(ImmutableMap.of("Title", "My New Article", "Title2", "Second Article"));

The method ImmutableMap.of() also has overloaded versions that can take up to 5 pairs of key-value parameters. Here’s what an example with 2 pairs of parameters would look like:

ImmutableMap.of("key1", "value1", "key2", "value2");

7. Conclusion

In this article we explored the various ways of initializing a Map, particularly to create empty, singleton, immutable and mutable maps. As we can see, there’s a huge improvement in this field since Java 9. 

Related posts:

Hướng dẫn Java Design Pattern – Flyweight
Spring Boot - Web Socket
String Processing with Apache Commons Lang 3
Java Program to Decode a Message Encoded Using Playfair Cipher
Removing Elements from Java Collections
Java Program to Check whether Directed Graph is Connected using DFS
A Guide to the Java ExecutorService
Java 8 Stream findFirst() vs. findAny()
Java Program to Implement LinkedList API
Java Program to Implement D-ary-Heap
Tìm hiểu về xác thực và phân quyền trong ứng dụng
Java Program to Find the Nearest Neighbor Using K-D Tree Search
Jackson – JsonMappingException (No serializer found for class)
Java List UnsupportedOperationException
Setting a Request Timeout for a Spring REST API
Java Program to Represent Graph Using 2D Arrays
An Introduction to Java.util.Hashtable Class
Introduction to Spring Cloud Rest Client with Netflix Ribbon
Spring 5 and Servlet 4 – The PushBuilder
Hướng dẫn Java Design Pattern – Singleton
Java 8 Predicate Chain
Java Program to Describe the Representation of Graph using Adjacency List
Giới thiệu Design Patterns
Spring Boot - Internationalization
The Difference Between map() and flatMap()
Hướng dẫn sử dụng biểu thức chính quy (Regular Expression) trong Java
Tính đóng gói (Encapsulation) trong java
Java Program to Implement Solovay Strassen Primality Test Algorithm
Hướng dẫn Java Design Pattern – Facade
Registration – Activate a New Account by Email
Converting a Stack Trace to a String in Java
Java Program to Create a Random Graph Using Random Edge Generation