- Как создать HashMap сразу с элементами?
- Initialize Map with Values in Java
- Using Map.of() and Map.ofEntries()
- Using Java Collections
- Initialize Map as an instance variable
- Initialize Map as a static variable
- Using Double Brace Initialization
- Using Stream Collectors.toMap()
- Conclusion
- See Also
- Roy’s musings
- Comments
- Popular posts from this blog
- A note on Java’s Calendar set() method
- The mysterious ORA-03111 error
- Note on allocationSize parameter of @SequenceGenerator while using JPA
Как создать HashMap сразу с элементами?
Проблема с созданием Map в том, что в отличие от других коллекций инициализация должна принять параметрами набор пар неопределенного размера. Поэтому varargs здесь не подходит.
Самый примитивный, многословный, но простой способ – добавить элементы сразу после создания. Для мапы-поля класса это можно сделать в конструкторе или блоке инициализации.
Map map = new HashMap<>();
map.put("one", "first");
map.put("two", "second");
>
Идиома double brace initialization. Компактная запись, которая расшифровывается компилятором как создание анонимного класса-наследника от HashMap , с добавлением элементов в блоке статической инициализации. Создание нового класса приводит к дополнительным накладным расходам, так делать не рекомендуется.
new HashMap() put("one", "first");
put("two", "second");
>>;
Для специальных случаев, пустой и одноэлементной неизменяемых мап, в классе Collections есть соответствующие фабричные методы emptyMap() и singletonMap(key, value) .
Удобно создавать HashMap из стрима. Коллектор Collectors.toMap(keyMapper, valueMapper) с помощью мапперов превратит объекты потока в ключи и значения.
В Java 9 наконец появились фабричные метод Map.of() , перегруженный для разного количества пар параметров, и Map.ofEntries() с varargs-аргументом.
До Java 9 подобное было реализовано во многих популярных библиотеках, например ImmutableMap.of в Guava и MapUtils.putAll() в Apache Commons.
Initialize Map with Values in Java
In this tutorial, we’ll learn different ways to initialize a Map with values in Java.
Using Map.of() and Map.ofEntries()
It is possible to initialize a Map with values in a single expression if you are using Java 9 or higher version using Map.of() and Map.ofEntries() method. This is shortest possible way so far.
Map.of()
Java 9 provides mutiple Map.of() overloaded methods to initialize a Map with upto 10 key-value pairs.
MapString, Integer> emptyMap = Map.of(); MapString, Integer> singletonMap = Map.of("A", 1); MapString, Integer> map = Map.of("A", 1, "B", 2, "C", 3);
Map.ofEntries()
If you have more than 10 key-value pairs to initialize, then you should use Map.ofEntries() method. This method has no limit and you can define any number of key-value pairs.
MapString, Integer> map = Map.ofEntries( Map.entry("A", 1), Map.entry("B", 2), Map.entry("C", 3), Map.entry("D", 4), Map.entry("E", 5), Map.entry("F", 6), Map.entry("G", 7), Map.entry("H", 8), Map.entry("I", 9), Map.entry("J", 10), Map.entry("K", 11), Map.entry("L", 12) ); map.put("M", 13); // Throw UnsupportedOperationException map.remove("A"); // Throw UnsupportedOperationException
Mutable Map
Thing to note that both Map.of() and Map.ofEntries() return an immutable map which means that adding or removing an element in Map result into java.lang.UnsupportedOperationException exception.
You can avoid this by creating a mutable map (by copying the immutable map to new HashMap ) in this way:-
MapString, Integer> mutableEmptyMap = new HashMap<>(Map.of()); MapString, Integer> mutableSingletonMap = new HashMap<>(Map.of("A", 1)); MapString, Integer> mutableMap = new HashMap<>(Map.ofEntries( Map.entry("A", 1), Map.entry("B", 2), Map.entry("C", 3), Map.entry("D", 4), Map.entry("E", 5), Map.entry("F", 6), Map.entry("G", 7), Map.entry("H", 8), Map.entry("I", 9), Map.entry("J", 10), Map.entry("K", 11), Map.entry("L", 12) )); mutableMap.put("M", 13); // It works! mutableMap.remove("A"); // It works!
Using Java Collections
Java Collections class provide methods to initialize emptyMap() , singletonMap() and unmodifiableMap() . Note that all these methods return immutable map
MapString, Integer> emptyMap = Collections.emptyMap(); MapString, Integer> singletonMap = Collections.singletonMap("A", 1); singletonMap.put("B", 2); // Throw UnsupportedOperationException singletonMap.remove("A"); // Throw UnsupportedOperationException MapString, Integer> mutableMap = new HashMap<>(singletonMap); mutableMap.put("B", 2); // It works! MapString, Integer> immutableMap = Collections.unmodifiableMap(mutableMap); immutableMap.put("B", 2); // Throw UnsupportedOperationException
Initialize Map as an instance variable
If you initialize a Map as an instance variable, keep the initialization in a constructor or instance initializer:-
public class MyClass MapString, Integer> instanceMap = new HashMap<>(); instanceMap.put("A", 1); instanceMap.put("B", 2); > >
Initialize Map as a static variable
If you initialize a Map as a static class variable, keep the initialization in a static initializer:-
public class MyClass static MapString, Integer> staticMap = new HashMap<>(); static staticMap.put("A", 1); staticMap.put("B", 2); > >
Using Double Brace Initialization
You can initialize map with values using Double Brace Initialization:-
MapString, Integer> map = new HashMap<>() < put("A", 1); put("B", 2); >>;
In Double brace initialization > , first brace creates a new Anonymous Inner Class, the second brace declares an instance initializer block that is run when the anonymous inner class is instantiated.
This approach is not recommended as it creates an extra class at each usage. It also holds hidden references to the enclosing instance and any captured objects. This may cause memory leaks or problems with serialization.
The alternative approach for this is to create a function to initialize a map:-
// It works for all Java versions, mutable map. MapString, Integer> map = createMap(); map.put("C", "3"); // It works! private static MapString, String> createMap() MapString, Integer> map = new HashMap<>(); map.put("A", 1); map.put("B", 2); return map; >
Using Stream Collectors.toMap()
We can also use Java 8 Stream API to initialize a Map with values.
When both key and value are of same type (e.g. String):-
MapString, String> mutableMap1 = Stream.of(new String[][] "A", "a">, "B", "b">, "C", "c"> >).collect(Collectors.toMap(p -> p[0], p -> p[1]));
When both key and value are of different type (e.g. String and Integer):-
MapString, Integer> mutableMap2 = Stream.of(new Object[][] "A", 1>, "B", 2>, "C", 3> >).collect(Collectors.toMap(p -> (String) p[0], p -> (Integer) p[1]));
Another approach that can easily accommodate different types for key and value involves creating a stream of map entries.
MapString, Integer> mutableMap3 = Stream.of( new AbstractMap.SimpleEntry<>("A", 1), new AbstractMap.SimpleEntry<>("B", 2), new AbstractMap.SimpleEntry<>("C", 3)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); MapString, Integer> mutableMap4 = Stream.of( new AbstractMap.SimpleImmutableEntry<>("A", 1), new AbstractMap.SimpleImmutableEntry<>("B", 2), new AbstractMap.SimpleImmutableEntry<>("C", 3)) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
The only difference between SimpleEntry and SimpleImmutableEntry is that you can set the value of SimpleEntry instance once initialized whereas set value of SimpleImmutableEntry after initialization throw UnsupportedOperationException .
Note that all the maps we have initialized using streams so far are mutable map means we can add or remove elements from them. You can initialize an immutable map using streams in this way:-
MapString, Integer> map5 = Stream.of( new AbstractMap.SimpleEntry<>("A", 1), new AbstractMap.SimpleEntry<>("B", 2), new AbstractMap.SimpleEntry<>("C", 3)) .collect(Collectors.collectingAndThen( Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue), Collections::unmodifiableMap ));
Conclusion
Let’s look at the summary of all the ways to initialize a Map with values:-
- Using Map.of() and Map.ofEntries() – Recommended this single line expression if you use Java 9 and above
- Using Java Collections – Works with all Java versions. Useful to define singleton map upto Java 8
- Using Double Brace Initialization — Avoid Double braces initialization. Create a method instead.
- Initialize Map as an instance variable — Recommended to initialize instance variable
- Initialize Map as a static variable — Recommended to initialize static variable
- Using Stream Collectors.toMap() – Too many lines of code. We can use other alternatives if possible to avoid boilerplate code.
See Also
Ashish Lahoti is a Software Engineer with 12+ years of experience in designing and developing distributed and scalable enterprise applications using modern practices. He is a technology enthusiast and has a passion for coding & blogging.
Roy’s musings
Question: What is the most efficient way of creating a collection (Set, List or Map) with a single element?
- Collections.singleton — To create a set that has only one element.
- Collections.singletonList — To create a list that has only one element.
- Collections.singletonMap — To crate a map that has only one entry.
Compare these methods with Collections.unmodifiableXXX() methods. The umodifiableXXX methods already accept a collection as an argument.
- Get link
- Other Apps
Comments
Popular posts from this blog
A note on Java’s Calendar set() method
Remember that the Calendar’s internal fields include year, month, date, hour, minutes, seconds, milliseconds and time zone. Whenever you are calling a set() method with multiple fields, like set(year, month, date), it will not affect the rest of the fields. Remember that there is no set() method with multiple fields available to set the milliseconds. If you would like to set the milliseconds, you must use set(Calendar.MILLISECOND, value). Likewise, if you are planning to set all the fields, its a good idea to reset all the fields using clear() method. This will clear milliseconds as well. Most of the times, millisecond field may not be of interest to you. But if you are going to use the UTC milliseconds, by calling getTimeInMillis(), then make sure you set the right values for milliseconds as well.
The mysterious ORA-03111 error
Recently one of the applications that I developed started throwing exceptions, that had the following message: SQL state [72000]; error code [1013]; ORA-03111: break received on communication channel When I googled around, I couldn’t come across anything useful. Sadly enough most of the sites just showed the documentation for that error, without any explanation from anyone experiencing that issues. So here you go, with the best possible explanation that I could come up with. My application sets two things on the connection that is throwing this exception: It sets the fetchSize to be 2500 rows It sets the query timeout to be 10 seconds The database server and the application are separated over a long latency network (actually there is a NetEm box that emulates the long latency between these two boxes) which has a latency characteristic of 50+/-5 milliseconds. This is the whole setup. It is important to understand how the timeout is handled by the Oracle client (in my case JDBC clien
Note on allocationSize parameter of @SequenceGenerator while using JPA
I ran into what I thought as an issue while I was using the sequence ID generation strategy in JPA. The JPA provider I am using is Hibernate. I think sharing my experience will save someone some time. To use a sequence (For e.g. Oracle sequence) to generate values and assign them to the ID field of your persistence object, you will following something like this: @Id @Column(name = «ITEM_ID») @GeneratedValue(strategy = GenerationType.SEQUENCE, generator=»ItemIdSeqGenerator») @SequenceGenerator(name=»ItemIdSeqGenerator», sequenceName=»ITEM_ID_SEQ», allocationSize=1) private long itemId; This means the following things: The @Id annotation says that the field itemId is a primary key. The @Column annotation says that the corresponding column in the database is ITEM_ID. The @GeneratedValue says that the value that needs to be populated in the itemId should be generated, while that object is persisted. The strategy to generate the value is to