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.
All Versions
In case you happen to need just a single entry: There is Collections.singletonMap("key", "value").
For Java Version 9 or higher:
Yes, this is possible now. In Java 9 a couple of factory methods have been added that simplify the creation of maps :
// this works for up to 10 elements:
Map<String, String> test1 = Map.of(
"a", "b",
"c", "d"
);
// this works for any number of elements:
import static java.util.Map.entry;
Map<String, String> test2 = Map.ofEntries(
entry("a", "b"),
entry("c", "d")
);
In the example above both test and test2 will be the same, just with different ways of expressing the Map. The Map.of method is defined for up to ten elements in the map, while the Map.ofEntries method will have no such limit.
Note that in this case the resulting map will be an immutable map. If you want the map to be mutable, you could copy it again, e.g. using mutableMap = new HashMap<>(Map.of("a", "b"));. Also note that in this case keys and values must not be null.
(See also JEP 269 and the Javadoc)
For up to Java Version 8:
No, you will have to add all the elements manually. You can use an initializer in an anonymous subclass to make the syntax a little bit shorter:
Map<String, String> myMap = new HashMap<String, String>() {{
put("a", "b");
put("c", "d");
}};
However, the anonymous subclass might introduce unwanted behavior in some cases. This includes for example:
It generates an additional class which increases memory consumption, disk space consumption and startup-time
In case of a non-static method: It holds a reference to the object the creating method was called upon. That means the object of the outer class cannot be garbage collected while the created map object is still referenced, thus blocking additional memory
Using a function for initialization will also enable you to generate a map in an initializer, but avoids nasty side-effects:
Map<String, String> myMap = createMap();
private static Map<String, String> createMap() {
Map<String,String> myMap = new HashMap<String,String>();
myMap.put("a", "b");
myMap.put("c", "d");
return myMap;
}
This is one way.
Map<String, String> h = new HashMap<String, String>() {{
put("a","b");
}};
However, you should be careful and make sure that you understand the above code (it creates a new class that inherits from HashMap). Therefore, you should read more here:
http://www.c2.com/cgi/wiki?DoubleBraceInitialization
, or simply use Guava:
Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
ImmutableMap.of works for up to 5 entries. Otherwise, use the builder: source.
If you allow 3rd party libs, you can use Guava's ImmutableMap to achieve literal-like brevity:
Map<String, String> test = ImmutableMap.of("k1", "v1", "k2", "v2");
This works for up to 5 key/value pairs, otherwise you can use its builder:
Map<String, String> test = ImmutableMap.<String, String>builder()
.put("k1", "v1")
.put("k2", "v2")
...
.build();
note that Guava's ImmutableMap implementation differs from Java's HashMap implementation (most notably it is immutable and does not permit null keys/values)
for more info, see Guava's user guide article on its immutable collection types
There is no direct way to do this - As of 2021, Java has no Map literals (yet - I think they were proposed for Java 8, but didn't make it).
Some people like this:
Map<String,String> test = new HashMap<String, String>(){{
put("test","test"); put("test","test");}};
This creates an anonymous subclass of HashMap, whose instance initializer puts these values. (By the way, a map can't contain twice the same value, your second put will overwrite the first one. I'll use different values for the next examples.)
The normal way would be this (for a local variable):
Map<String,String> test = new HashMap<String, String>();
test.put("test","test");
test.put("test1","test2");
If your test map is an instance variable, put the initialization in a constructor or instance initializer:
Map<String,String> test = new HashMap<String, String>();
{
test.put("test","test");
test.put("test1","test2");
}
If your test map is a class variable, put the initialization in a static initializer:
static Map<String,String> test = new HashMap<String, String>();
static {
test.put("test","test");
test.put("test1","test2");
}
If you want your map to never change, you should after the initialization wrap your map by Collections.unmodifiableMap(...). You can do this in a static initializer too:
static Map<String,String> test;
{
Map<String,String> temp = new HashMap<String, String>();
temp.put("test","test");
temp.put("test1","test2");
test = Collections.unmodifiableMap(temp);
}
(I'm not sure if you can now make test final ... try it out and report here.)
Since Java 9, you also have the Map.of(...) and Map.ofEntries() syntax, as explained in the answer from yankee.
Map<String,String> test = new HashMap<String, String>()
{
{
put(key1, value1);
put(key2, value2);
}
};
An alternative, using plain Java 7 classes and varargs: create a class HashMapBuilder with this method:
public static HashMap<String, String> build(String... data){
HashMap<String, String> result = new HashMap<String, String>();
if(data.length % 2 != 0)
throw new IllegalArgumentException("Odd number of arguments");
String key = null;
Integer step = -1;
for(String value : data){
step++;
switch(step % 2){
case 0:
if(value == null)
throw new IllegalArgumentException("Null key value");
key = value;
continue;
case 1:
result.put(key, value);
break;
}
}
return result;
}
Use the method like this:
HashMap<String,String> data = HashMapBuilder.build("key1","value1","key2","value2");
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 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;
}
}
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]));
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.
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 .
Related
I would like to have a method that maps a List to a NavigableMap. The method call expects an parameter that is used as map key. This parameter is an attribute of the list objects.
Something like this, so both calls are ok:
List<MyObject> list = new ArrayList<>();
NavigableMap<String, MyObject> stringKeyMap = asNavMap(list, MyObject:.getString());
NavigableMap<Date, MyObject> dateKeyMap = asNavMap(list, MyObject::getDate());
I dont know how to define the second parameter (MyObject::getDate()). Do I have to use a lambda expression (p -> p.getDate()) or something like Predicate or Function?
I've tried to derive a solution from Approach 8 (or simular) from http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html, but I don't know how to do.
This is what I have done so far:
The concrete implementation:
public class ConcreteConverter {
public static NavigableMap<Integer, Pair<Integer, String>> asNavMap(List<Pair<Integer, String>> pairs) {
NavigableMap<Integer, Pair<Integer, String>> navMap = new TreeMap<>();
for (Pair<Integer, String> pair : pairs) {
navMap.put(pair.getKey(), pair);
}
return navMap;
}
public static void main(String[] args) {
List<Pair<Integer, String>> pairs = new ArrayList<>();
pairs.add(new Pair<Integer, String>(1, "one"));
NavigableMap<Integer, Pair<Integer, String>> map = ConcreteConverter.asNavMap(pairs);
}
}
class Pair<K, V> {
K key;
V val;
// constructor, getter, setter
}
Here I stuck (??? is an attribute of the Pair object):
public static <K, V> NavigableMap<K, V> asNavMap(List<V> items, ???) {
NavigableMap<K, V> navMap = new TreeMap<>();
for (V item : items) {
navMap.put(???, item);
}
return navMap;
}
Please notice I have barely experiences writing generic methods or using lambda functions/interfaces.
Any help is appreciated.
Edit 1
As Nick Vanderhofen mentioned I didn't clarify the search for a generic solution.
You can do that with a Function. You keep the code you wanted:
List<MyObject> list = new ArrayList<>();
NavigableMap<String, MyObject> stringKeyMap = asNavMap(list, MyObject::getKey);
The method asNavMap can then take a Function:
private NavigableMap<String,MyObject> asNavMap(List<MyObject> list, Function<MyObject, String> getKey) {
//the actual mapping goes here
}
The getKey method you are specifying can either be a simple getter on the MyObject:
public String getKey(){
return key;
}
Or you could create a static method to get the same result:
public static String getKey(MyObject myObject){
return myObject.getKey();
}
To apply the function you can just use the apply method:
String key = getKey.apply(someObject);
For the actual mapping implementation you can keep your for loop, or you could rewrite it using java 8 and re-use the Function that you got as a parameter in the collector. However, since you want a TreeMap, the syntax is quite verbose:
items.stream().collect(Collectors.toMap(mapper, Function.identity(), (a,b) -> a, TreeMap::new));
Just figured out a working solution!
Still reading http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#approach7 I've tried to use Function, and now this is my solution:
public static <K, V> NavigableMap<K, V> asNavigableMap(List<V> items, Function<V, K> mapper) {
NavigableMap<K, V> navMap = new TreeMap<>();
for (V item : items)
navMap.put(mapper.apply(item), item);
return navMap;
}
And these calls work:
List<Pair<Integer, String>> pairs = new ArrayList<>();
pairs.add(new Pair<Integer, String>(1, "one"));
NavigableMap<Integer, Pair<Integer, String>> navI2P1 = GenericConverter.asNavigableMap(pairs, Pair::getKey);
NavigableMap<String, Pair<Integer, String>> navI2P2 = GenericConverter.asNavigableMap(pairs, Pair::getVal);
It was hard for me to understand the Function functional interface and the apply method.
Thanks to anyone!
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.
All Versions
In case you happen to need just a single entry: There is Collections.singletonMap("key", "value").
For Java Version 9 or higher:
Yes, this is possible now. In Java 9 a couple of factory methods have been added that simplify the creation of maps :
// this works for up to 10 elements:
Map<String, String> test1 = Map.of(
"a", "b",
"c", "d"
);
// this works for any number of elements:
import static java.util.Map.entry;
Map<String, String> test2 = Map.ofEntries(
entry("a", "b"),
entry("c", "d")
);
In the example above both test and test2 will be the same, just with different ways of expressing the Map. The Map.of method is defined for up to ten elements in the map, while the Map.ofEntries method will have no such limit.
Note that in this case the resulting map will be an immutable map. If you want the map to be mutable, you could copy it again, e.g. using mutableMap = new HashMap<>(Map.of("a", "b"));. Also note that in this case keys and values must not be null.
(See also JEP 269 and the Javadoc)
For up to Java Version 8:
No, you will have to add all the elements manually. You can use an initializer in an anonymous subclass to make the syntax a little bit shorter:
Map<String, String> myMap = new HashMap<String, String>() {{
put("a", "b");
put("c", "d");
}};
However, the anonymous subclass might introduce unwanted behavior in some cases. This includes for example:
It generates an additional class which increases memory consumption, disk space consumption and startup-time
In case of a non-static method: It holds a reference to the object the creating method was called upon. That means the object of the outer class cannot be garbage collected while the created map object is still referenced, thus blocking additional memory
Using a function for initialization will also enable you to generate a map in an initializer, but avoids nasty side-effects:
Map<String, String> myMap = createMap();
private static Map<String, String> createMap() {
Map<String,String> myMap = new HashMap<String,String>();
myMap.put("a", "b");
myMap.put("c", "d");
return myMap;
}
This is one way.
Map<String, String> h = new HashMap<String, String>() {{
put("a","b");
}};
However, you should be careful and make sure that you understand the above code (it creates a new class that inherits from HashMap). Therefore, you should read more here:
http://www.c2.com/cgi/wiki?DoubleBraceInitialization
, or simply use Guava:
Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
ImmutableMap.of works for up to 5 entries. Otherwise, use the builder: source.
If you allow 3rd party libs, you can use Guava's ImmutableMap to achieve literal-like brevity:
Map<String, String> test = ImmutableMap.of("k1", "v1", "k2", "v2");
This works for up to 5 key/value pairs, otherwise you can use its builder:
Map<String, String> test = ImmutableMap.<String, String>builder()
.put("k1", "v1")
.put("k2", "v2")
...
.build();
note that Guava's ImmutableMap implementation differs from Java's HashMap implementation (most notably it is immutable and does not permit null keys/values)
for more info, see Guava's user guide article on its immutable collection types
There is no direct way to do this - As of 2021, Java has no Map literals (yet - I think they were proposed for Java 8, but didn't make it).
Some people like this:
Map<String,String> test = new HashMap<String, String>(){{
put("test","test"); put("test","test");}};
This creates an anonymous subclass of HashMap, whose instance initializer puts these values. (By the way, a map can't contain twice the same value, your second put will overwrite the first one. I'll use different values for the next examples.)
The normal way would be this (for a local variable):
Map<String,String> test = new HashMap<String, String>();
test.put("test","test");
test.put("test1","test2");
If your test map is an instance variable, put the initialization in a constructor or instance initializer:
Map<String,String> test = new HashMap<String, String>();
{
test.put("test","test");
test.put("test1","test2");
}
If your test map is a class variable, put the initialization in a static initializer:
static Map<String,String> test = new HashMap<String, String>();
static {
test.put("test","test");
test.put("test1","test2");
}
If you want your map to never change, you should after the initialization wrap your map by Collections.unmodifiableMap(...). You can do this in a static initializer too:
static Map<String,String> test;
{
Map<String,String> temp = new HashMap<String, String>();
temp.put("test","test");
temp.put("test1","test2");
test = Collections.unmodifiableMap(temp);
}
(I'm not sure if you can now make test final ... try it out and report here.)
Since Java 9, you also have the Map.of(...) and Map.ofEntries() syntax, as explained in the answer from yankee.
Map<String,String> test = new HashMap<String, String>()
{
{
put(key1, value1);
put(key2, value2);
}
};
An alternative, using plain Java 7 classes and varargs: create a class HashMapBuilder with this method:
public static HashMap<String, String> build(String... data){
HashMap<String, String> result = new HashMap<String, String>();
if(data.length % 2 != 0)
throw new IllegalArgumentException("Odd number of arguments");
String key = null;
Integer step = -1;
for(String value : data){
step++;
switch(step % 2){
case 0:
if(value == null)
throw new IllegalArgumentException("Null key value");
key = value;
continue;
case 1:
result.put(key, value);
break;
}
}
return result;
}
Use the method like this:
HashMap<String,String> data = HashMapBuilder.build("key1","value1","key2","value2");
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 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;
}
}
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]));
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.
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 .
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.
All Versions
In case you happen to need just a single entry: There is Collections.singletonMap("key", "value").
For Java Version 9 or higher:
Yes, this is possible now. In Java 9 a couple of factory methods have been added that simplify the creation of maps :
// this works for up to 10 elements:
Map<String, String> test1 = Map.of(
"a", "b",
"c", "d"
);
// this works for any number of elements:
import static java.util.Map.entry;
Map<String, String> test2 = Map.ofEntries(
entry("a", "b"),
entry("c", "d")
);
In the example above both test and test2 will be the same, just with different ways of expressing the Map. The Map.of method is defined for up to ten elements in the map, while the Map.ofEntries method will have no such limit.
Note that in this case the resulting map will be an immutable map. If you want the map to be mutable, you could copy it again, e.g. using mutableMap = new HashMap<>(Map.of("a", "b"));. Also note that in this case keys and values must not be null.
(See also JEP 269 and the Javadoc)
For up to Java Version 8:
No, you will have to add all the elements manually. You can use an initializer in an anonymous subclass to make the syntax a little bit shorter:
Map<String, String> myMap = new HashMap<String, String>() {{
put("a", "b");
put("c", "d");
}};
However, the anonymous subclass might introduce unwanted behavior in some cases. This includes for example:
It generates an additional class which increases memory consumption, disk space consumption and startup-time
In case of a non-static method: It holds a reference to the object the creating method was called upon. That means the object of the outer class cannot be garbage collected while the created map object is still referenced, thus blocking additional memory
Using a function for initialization will also enable you to generate a map in an initializer, but avoids nasty side-effects:
Map<String, String> myMap = createMap();
private static Map<String, String> createMap() {
Map<String,String> myMap = new HashMap<String,String>();
myMap.put("a", "b");
myMap.put("c", "d");
return myMap;
}
This is one way.
Map<String, String> h = new HashMap<String, String>() {{
put("a","b");
}};
However, you should be careful and make sure that you understand the above code (it creates a new class that inherits from HashMap). Therefore, you should read more here:
http://www.c2.com/cgi/wiki?DoubleBraceInitialization
, or simply use Guava:
Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
ImmutableMap.of works for up to 5 entries. Otherwise, use the builder: source.
If you allow 3rd party libs, you can use Guava's ImmutableMap to achieve literal-like brevity:
Map<String, String> test = ImmutableMap.of("k1", "v1", "k2", "v2");
This works for up to 5 key/value pairs, otherwise you can use its builder:
Map<String, String> test = ImmutableMap.<String, String>builder()
.put("k1", "v1")
.put("k2", "v2")
...
.build();
note that Guava's ImmutableMap implementation differs from Java's HashMap implementation (most notably it is immutable and does not permit null keys/values)
for more info, see Guava's user guide article on its immutable collection types
There is no direct way to do this - As of 2021, Java has no Map literals (yet - I think they were proposed for Java 8, but didn't make it).
Some people like this:
Map<String,String> test = new HashMap<String, String>(){{
put("test","test"); put("test","test");}};
This creates an anonymous subclass of HashMap, whose instance initializer puts these values. (By the way, a map can't contain twice the same value, your second put will overwrite the first one. I'll use different values for the next examples.)
The normal way would be this (for a local variable):
Map<String,String> test = new HashMap<String, String>();
test.put("test","test");
test.put("test1","test2");
If your test map is an instance variable, put the initialization in a constructor or instance initializer:
Map<String,String> test = new HashMap<String, String>();
{
test.put("test","test");
test.put("test1","test2");
}
If your test map is a class variable, put the initialization in a static initializer:
static Map<String,String> test = new HashMap<String, String>();
static {
test.put("test","test");
test.put("test1","test2");
}
If you want your map to never change, you should after the initialization wrap your map by Collections.unmodifiableMap(...). You can do this in a static initializer too:
static Map<String,String> test;
{
Map<String,String> temp = new HashMap<String, String>();
temp.put("test","test");
temp.put("test1","test2");
test = Collections.unmodifiableMap(temp);
}
(I'm not sure if you can now make test final ... try it out and report here.)
Since Java 9, you also have the Map.of(...) and Map.ofEntries() syntax, as explained in the answer from yankee.
Map<String,String> test = new HashMap<String, String>()
{
{
put(key1, value1);
put(key2, value2);
}
};
An alternative, using plain Java 7 classes and varargs: create a class HashMapBuilder with this method:
public static HashMap<String, String> build(String... data){
HashMap<String, String> result = new HashMap<String, String>();
if(data.length % 2 != 0)
throw new IllegalArgumentException("Odd number of arguments");
String key = null;
Integer step = -1;
for(String value : data){
step++;
switch(step % 2){
case 0:
if(value == null)
throw new IllegalArgumentException("Null key value");
key = value;
continue;
case 1:
result.put(key, value);
break;
}
}
return result;
}
Use the method like this:
HashMap<String,String> data = HashMapBuilder.build("key1","value1","key2","value2");
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 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;
}
}
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]));
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.
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 currently have a setup where the data structure is an ArrayList with each key containing a HashMap for each key in ArrayList. What I'm trying to do is be able to sort by key or keys within the HashMap itself. In my research, most advice seems to be to use Collections.sort(ArrayList, comparatorFunction()) and then build a custom Comparatorfunction to do the sorting, but as a complete noob as to how to build a Comparator...I don't even know where to start, much less build one that' I'm sure is not a simple setup. Anyone happen to know of some resources that would be useful to address this kind of functionality?
EDIT: Sorry some sample structure would be helpful.
if you called arrayList.get(0) and did a System.out.println on it, it would return say {town=Toronto, population=2,500,000, age=147}, what I'm trying to do is have it so I could say order the ArrayList by say population, and then age for example.
Usually in situations like this the job of the Comparator is to simple return the value of a compare from something else. For example, here is a Comparator that will alphabetize Fonts:
class FontAlphabetizer
implements Comparator<Font> {
#Override
public int compare(Font font1, Font font2) {
return font1.getName().compareTo(font2.getName());
}
}
That's actually pretty simple: getName returns a String and all we do is return the value of String's compareTo method.
Here it seems like what you have is an ArrayList<Map> and you want to sort the ArrayList based on a chosen value from the Map. So what you need is a Comparator<Map>. And you need to give the Comparator the key for the corresponding value that you want to sort by. This can be expressed generically like the following:
class MapValueComparator<K, V extends Comparable<V>>
implements Comparator<Map<K, V>> {
final K key;
MapValueComparator(K key) {
this.key = key;
}
#Override
public int compare(Map<K, V> map1, Map<K, V> map2) {
return map1.get(key).compareTo(map2.get(key));
}
}
That is a Comparator that compares Maps and it's specified in the declaration there that the Map's values must also be Comparable. It compares based on the value retrieved from the given key.
So for example if we have an ArrayList<Map<String, String>>, we can sort by the value from "town" like this:
static void sortByTown(List<Map<String, String>> list) {
Collections.sort(list, new MapValueComparator<String, String>("town"));
}
The hiccup is that you say you have town=Toronto, population=2,500,000 which indicates that the population you want to sort by is a String (since presumably it's in the same map as Toronto). Comparing population as String probably isn't desired because it will sort lexicographically (50 comes after 2,500,000 because 5 comes after 2). In that case the generic version might not work because you need to take an extra step of converting the value to a number.
class PopulationComparator
implements Comparator<Map<String, String>> {
#Override
public int compare(Map<String, String> map1, Map<String, String> map2) {
final Long pop1 = Long.valueOf(map1.get("population"));
final Long pop2 = Long.valueOf(map2.get("population"));
return pop1.compareTo(pop2);
}
}
(And as a side note if your population contains commas you'd need to format that before parsing it to a number. You can use replaceAll("\\D", "") to remove all non digits from a String.)
This is also a case where it could be advantageous to create a class for this instead of using a Map. Then you could have the numerical fields be number types. If you had a class, the comparison would be mostly the same though: just returning a comparison of a chosen field.
Custom Comparator can be used to define the way the objects of your class can be compared. It has the following Syntax :
public class CustomComparator implements Comparator<MyObjectType>
{
public int compare(MyObjectType ob1 , MyObjectType ob2)
{
//code to compare the 2 objects
}
}
Refer to the following link for information on creating a Comparator class for custom sorting of elements in collection : link
Here is what you are looking for:
final List<Map<String, Object>> towns = new ArrayList<Map<String, Object>>();
final Map<String, Object> toronto = new HashMap<String, Object>();
toronto.put("town", "Toronto");
toronto.put("population", 2500000);
toronto.put("age", 147);
towns.add(toronto);
final Map<String, Object> ottawa = new HashMap<String, Object>();
ottawa.put("town", "Ottawa");
ottawa.put("population", 883000);
ottawa.put("age", 159);
towns.add(ottawa);
final Map<String, Object> montreal = new HashMap<String, Object>();
montreal.put("town", "Montreal");
montreal.put("population", 1600000);
montreal.put("age", 372);
towns.add(montreal);
final Map<String, Object> quebec = new HashMap<String, Object>();
quebec.put("town", "Quebec City");
quebec.put("population", 600000);
quebec.put("age", 406);
towns.add(quebec);
final Map<String, Object> vancouver = new HashMap<String, Object>();
vancouver.put("town", "Vancouver");
vancouver.put("population", 600000);
vancouver.put("age", 128);
towns.add(vancouver);
Collections.sort(towns, new Comparator<Map<String, Object>>() {
#Override
public int compare(final Map<String, Object> o1, final Map<String, Object> o2) {
if (o1.get("population") instanceof Integer && o2.get("population") instanceof Integer && !((Integer)o1.get("population")).equals((Integer)o2.get("population"))) {
return ((Integer)o1.get("population")).compareTo((Integer)o2.get("population"));
}
if (o1.get("age") instanceof Integer && o2.get("age") instanceof Integer) {
return ((Integer)o1.get("age")).compareTo((Integer)o2.get("age"));
}
// Default if there is no population/no age, shouldn't happen.
// TODO : do something else.
return o1.toString().compareTo(o2.toString());
}
});
for (final Map<String, Object> town: towns) {
System.out.println(town.get("population")+"\t"+town.get("age")+"\t"+town.get("town"));
}
The first part of the code is just to create the ArrayList according to what you said you have, then we use a custom Comparator to sort the List, and print the result.
Here is the output:
600000 128 Vancouver
600000 406 Quebec City
883000 159 Ottawa
1600000 372 Montreal
2500000 147 Toronto
As you can see, it's sorted by population, then by age.
But, maybe the best solution would be to create an object Town, with three fields (name, population and age), and use this object instead of the HashMaps.
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.
All Versions
In case you happen to need just a single entry: There is Collections.singletonMap("key", "value").
For Java Version 9 or higher:
Yes, this is possible now. In Java 9 a couple of factory methods have been added that simplify the creation of maps :
// this works for up to 10 elements:
Map<String, String> test1 = Map.of(
"a", "b",
"c", "d"
);
// this works for any number of elements:
import static java.util.Map.entry;
Map<String, String> test2 = Map.ofEntries(
entry("a", "b"),
entry("c", "d")
);
In the example above both test and test2 will be the same, just with different ways of expressing the Map. The Map.of method is defined for up to ten elements in the map, while the Map.ofEntries method will have no such limit.
Note that in this case the resulting map will be an immutable map. If you want the map to be mutable, you could copy it again, e.g. using mutableMap = new HashMap<>(Map.of("a", "b"));. Also note that in this case keys and values must not be null.
(See also JEP 269 and the Javadoc)
For up to Java Version 8:
No, you will have to add all the elements manually. You can use an initializer in an anonymous subclass to make the syntax a little bit shorter:
Map<String, String> myMap = new HashMap<String, String>() {{
put("a", "b");
put("c", "d");
}};
However, the anonymous subclass might introduce unwanted behavior in some cases. This includes for example:
It generates an additional class which increases memory consumption, disk space consumption and startup-time
In case of a non-static method: It holds a reference to the object the creating method was called upon. That means the object of the outer class cannot be garbage collected while the created map object is still referenced, thus blocking additional memory
Using a function for initialization will also enable you to generate a map in an initializer, but avoids nasty side-effects:
Map<String, String> myMap = createMap();
private static Map<String, String> createMap() {
Map<String,String> myMap = new HashMap<String,String>();
myMap.put("a", "b");
myMap.put("c", "d");
return myMap;
}
This is one way.
Map<String, String> h = new HashMap<String, String>() {{
put("a","b");
}};
However, you should be careful and make sure that you understand the above code (it creates a new class that inherits from HashMap). Therefore, you should read more here:
http://www.c2.com/cgi/wiki?DoubleBraceInitialization
, or simply use Guava:
Map<String, Integer> left = ImmutableMap.of("a", 1, "b", 2, "c", 3);
ImmutableMap.of works for up to 5 entries. Otherwise, use the builder: source.
If you allow 3rd party libs, you can use Guava's ImmutableMap to achieve literal-like brevity:
Map<String, String> test = ImmutableMap.of("k1", "v1", "k2", "v2");
This works for up to 5 key/value pairs, otherwise you can use its builder:
Map<String, String> test = ImmutableMap.<String, String>builder()
.put("k1", "v1")
.put("k2", "v2")
...
.build();
note that Guava's ImmutableMap implementation differs from Java's HashMap implementation (most notably it is immutable and does not permit null keys/values)
for more info, see Guava's user guide article on its immutable collection types
There is no direct way to do this - As of 2021, Java has no Map literals (yet - I think they were proposed for Java 8, but didn't make it).
Some people like this:
Map<String,String> test = new HashMap<String, String>(){{
put("test","test"); put("test","test");}};
This creates an anonymous subclass of HashMap, whose instance initializer puts these values. (By the way, a map can't contain twice the same value, your second put will overwrite the first one. I'll use different values for the next examples.)
The normal way would be this (for a local variable):
Map<String,String> test = new HashMap<String, String>();
test.put("test","test");
test.put("test1","test2");
If your test map is an instance variable, put the initialization in a constructor or instance initializer:
Map<String,String> test = new HashMap<String, String>();
{
test.put("test","test");
test.put("test1","test2");
}
If your test map is a class variable, put the initialization in a static initializer:
static Map<String,String> test = new HashMap<String, String>();
static {
test.put("test","test");
test.put("test1","test2");
}
If you want your map to never change, you should after the initialization wrap your map by Collections.unmodifiableMap(...). You can do this in a static initializer too:
static Map<String,String> test;
{
Map<String,String> temp = new HashMap<String, String>();
temp.put("test","test");
temp.put("test1","test2");
test = Collections.unmodifiableMap(temp);
}
(I'm not sure if you can now make test final ... try it out and report here.)
Since Java 9, you also have the Map.of(...) and Map.ofEntries() syntax, as explained in the answer from yankee.
Map<String,String> test = new HashMap<String, String>()
{
{
put(key1, value1);
put(key2, value2);
}
};
An alternative, using plain Java 7 classes and varargs: create a class HashMapBuilder with this method:
public static HashMap<String, String> build(String... data){
HashMap<String, String> result = new HashMap<String, String>();
if(data.length % 2 != 0)
throw new IllegalArgumentException("Odd number of arguments");
String key = null;
Integer step = -1;
for(String value : data){
step++;
switch(step % 2){
case 0:
if(value == null)
throw new IllegalArgumentException("Null key value");
key = value;
continue;
case 1:
result.put(key, value);
break;
}
}
return result;
}
Use the method like this:
HashMap<String,String> data = HashMapBuilder.build("key1","value1","key2","value2");
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 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;
}
}
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]));
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.
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 .