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.
For efficiency, if the passed map is already truly immutable, the copyOf
method returns a reference to the original without manufacturing a new map.
About Collections.unmodifiableMap
Tip: Prefer Map.copyOf
over Collections.unmodifiableMap
if you need/expect a truly immutable map.
The Collections
method produces a view onto the original map, a mere wrapper, not a true copy. The upside: Being a mere view has the benefit of conserving memory. The downside: Modifications to the original show through.
If the original map gets modified (put
, remove
, etc.), the supposedly-unmodifiable will actually show the modification. In contrast, the Map.copyOf
does indeed produce a true copy of the passed map if not already truly immutable.
record Person( String name ) { }
Person alice = new Person ( "Alice" );
Person bob = new Person ( "Bob" );
Map < DayOfWeek, Person > weekendWorkerMutable = HashMap.newHashMap ( 2 );
weekendWorkerMutable.put ( DayOfWeek.SATURDAY , bob );
weekendWorkerMutable.put ( DayOfWeek.SUNDAY , bob );
System.out.println ( "weekendWorkerMutable = " + weekendWorkerMutable );
Map < DayOfWeek, Person > weekendWorkerSupposedlyUnmodifiable = Collections.unmodifiableMap ( weekendWorkerMutable );
System.out.println ( "weekendWorkerSupposedlyUnmodifiable = " + weekendWorkerSupposedlyUnmodifiable );
Map < DayOfWeek, Person > trueCopy = Map.copyOf ( weekendWorkerSupposedlyUnmodifiable );
System.out.println ( "trueCopy = " + trueCopy );
weekendWorkerMutable.put ( DayOfWeek.SATURDAY , alice ); // <--- Modify the original.
System.out.println ( " ----- After mutating the original mutable map ----- " );
System.out.println ( "weekendWorkerSupposedlyUnmodifiable = " + weekendWorkerSupposedlyUnmodifiable );
System.out.println ( "trueCopy = " + trueCopy );
When run:
- We see that the supposedly unmodifiable is indeed modifiable indirectly, by modifying the original map on which the unmodifiable map is a view.
- In contrast, the
copyOf
method produces a true copy, showing Bob as working the entire weekend even after assigning Alice.
weekendWorkerMutable = {SATURDAY=Person[name=Bob], SUNDAY=Person[name=Bob]}
weekendWorkerSupposedlyUnmodifiable = {SATURDAY=Person[name=Bob], SUNDAY=Person[name=Bob]}
trueCopy = {SUNDAY=Person[name=Bob], SATURDAY=Person[name=Bob]}
----- After mutating the original mutable map -----
weekendWorkerSupposedlyUnmodifiable = {SATURDAY=Person[name=Alice], SUNDAY=Person[name=Bob]}
trueCopy = {SUNDAY=Person[name=Bob], SATURDAY=Person[name=Bob]}
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.
to.map("myKey", "myValue")
. It very simple and has a lot of other useful features – Veniceveninstatic
while this Question is asking about instantiating by literal syntax. Voting to re-open. Perhaps this Question is a duplicate of some other Question; if so, re-open and close again by linking to a Question that is truly an original of this. – Abuzz