Can I declare and initialize a map in java with a literal?

Viewed 91

Is it possible to declare and initialize a Hashtable or Map, so we don't have to declare and initialize on two different steps?

// declare first
Hashtable<String, Integer> numbers = new Hashtable<String, Integer>(3);

// then initialize
numbers.put("one", 1);
numbers.put("two", 2);
numbers.put("three", 3);

In ruby and javascript, for example, there are literals to define those:

numbers = {
  one: 1,
  two: 2,
  three: 3,
}
3 Answers

For Java 9 and beyond, you can make use of Map#of, as well as Map#ofEntries with Map#entry:

Map<String, String> example = Map.ofEntries(
    Map.entry("1", "one"),
    Map.entry("2", "two"),
    Map.entry("3", "three"),
    //...
);

For shorter literals (10 or less entries), you can use Map#of directly (albeit with a slightly confusing syntax of Key1, Value1, Key2, Value2, ...):

Map<String, String> example = Map.of("1", "one", "2", "two");

Prior to Java 9, this would typically only be needed for initializing constant Map objects, for which a static initializer block is also useful:

private static final Map<String, String> MY_CONSTANT;

static {
    Map<String, String> setup = new HashMap<>();
    setup.put("1", "one");
    setup.put("2", "two");
    setup.put("3", "three");
    MY_CONSTANT = Collections.unmodifiableMap(setup);
}

This will allow you to create a constant Map while allowing you to modify the map before it is set.

As pointed out by @Pshemo, there are numerous reasons to avoid double-brace initialization, however the notable exception to the "rule" is typically in constants much like the above example (as numerous anonymous classes would not be made). However, it is still possible to work around the need for double-brace initialization even this case, so it is overall best to avoid if possible. The only case I can think of this not being possible is within the context of a constant within an enum class, where the double-brace init may truly be the only true way to make this work.

Firstly, you shouldn't use Hashtable class - it's legacy. If you need general purpose implementation of the Map interface, use HashMap instead.

In case when you need to initialize a general purpose Map with a few entries, you can use one of the overloaded versions of Map.of() method which is available starting with Java 9. It allows providing up to 10 key-value pairs, if you need more you can use Map.ofEntries(), which takes a varargs of map entries. You can create a map entry using another Java 9 method Map.entry().

Example:

public static final Map<String, Integer> numbers = 
    Map.of("one", 1, "two", 2, "three", 3);

Note these maps are designed to be 1 immutable, 2 moderate in size, although with Map.ofEntries() technically you could pass as many entries as you wish (especially by providing an array instead of varargs) you're not advised to do that, otherwise performance might disappoint you (because implementation isn't hash-based, sacrifices performance in order to reduce memory consumption, time complexity get() operation is linear, opposed to HashMap which retrieves a value by the given key in constant time).

Spiking of other alternatives doubly-brace initialization, which creates an instance of anonymous inner class, is indisputably a discouraged practice.

Static and instance initializer blocks have no downsides from the technical point of view, they allow you to keep the variables final if you need that. But some might argue that from the perspective of clean coding, initializer blocks are not very great.

Another possibility you might consider is to initialize a map inline using Stream API (for instance, when map-data can be expressed as a sequence that is easy to generate).

Solution 1 (Issue: Creates anonymous classes that cannot be garbage collected

Here's how you can do this -

  Map<String, Integer> myMap = new HashMap<String, Integer>() {{
  put("a", 1);
  put("c", 2);
  }};

Output

{a=1, c=2}

works same way with Hashtable

As suggested by @Pshemo

Solution 2: Using Map.Of (Immutable map, will throw UnsupportedOperationException if you try to add, delete and update elements in the map

 Map<String, String> map
    = Map.of("1", "Stack",
             "2", "Overflow",
             "3", "Java");

  System.out.println(map);
}

Output

{3=Java, 1=Stack, 2=Overflow}

Related