Jay Taylor's notes

back to listing index

java - How to directly initialize a HashMap (in a literal way)? - Stack Overflow

[web search]
Original source (stackoverflow.com)
Tags: java jvm howto data-structures java-8 map hash-map inline-map-initialization dict java-9 stackoverflow.com
Clipped on: 2021-01-20

Asked 9 years, 6 months ago
Active 3 days ago
Viewed 1.4m times
1227

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.

Image (Asset 2/36) alt=
  • Closely related: stackoverflow.com/questions/507602/… (Both questions are about initialising a constant map with static, final values.) – Jonik Jul 18 '13 at 11:27
  • 1
    Not a duplicate of the linked Question. That Question is about static 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. – Basil Bourque Dec 17 '19 at 2:13
  • 1555

    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"));

    (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;
    }
    
    Image (Asset 3/36) alt=
    This won't work if you want to initial the elements in a function... – Michael Aug 15 '15 at 23:49
  • 10
    @Michael: Well yes, if you want to use a function than you cannot use a not-function. But why do you want to? – yankee Aug 16 '15 at 8:05
  • 6
    and for the cases when you need a Map with a single entry there's Collections.singletonMap() :) – skwisgaar Aug 16 '17 at 17:47
  • 3
    Now that stable Java 9 has been released, I prefer this link for Javadoc. And +1 because one less dependency! – Franklin Yu Oct 3 '17 at 14:14
  • 3
    Where is Java 9 entry documented? – Brent Bradburn Jul 27 '18 at 16:42
  • 1063
    +150

    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.

    Image (Asset 4/36) alt=
    It works but it's ugly and has invisible side effects that the user should understand before doing it - for example, generating an entire anonymous class on the spot. – jprete Jul 23 '11 at 18:48
  • 97
    yep, that is way I wrote about being careful and gave a link to the description. – gregory561 Jul 23 '11 at 18:50
  • 6
    Great link. The reference in that link to GreencoddsTenthRuleOfProgramming is worth the read. – michaelok May 16 '13 at 21:10
  • 20
    can you add "as ImmutableMap.builder.put("k1","v1").put("k2","v2").build()" as the "of" method is limited to 5 pairs at maximum ? – kommradHomer Oct 15 '14 at 9:15
  • 3
  • 352

    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
    answered Jun 28 '13 at 22:03
    Image (Asset 5/36) alt=
    Also, guava has ImmutableMap.builder.put("k1","v1").put("k2","v2").build(); – Xetius Oct 9 '13 at 13:50
  • 17
    ImmutableMap is not the same as a HashMap, as it will fail on null values, whereas map HashMap will not. – Gewthen Mar 6 '14 at 19:04
  • 2
    Just to help others that might face this issue. You have to type the builder to make it a Map<String, String>, like this: Map<String,String> test = ImmutableMap.<String,String>builder().put("k1", "v1").put("k2", "v2").build(); – Thiago Mar 9 '15 at 20:35
  • this is awesome Jens! – Gaurav Mar 8 '19 at 13:58
  • 108

    There is no direct way to do this - Java has no Map literals (yet - I think they were proposed for Java 8).

    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.)

    answered Jul 23 '11 at 18:48
    Image (Asset 6/36) alt=
    Map<String,String> test = new HashMap<String, String>()
    {
        {
            put(key1, value1);
            put(key2, value2);
        }
    };
    
    answered Jul 23 '11 at 18:47
    Image (Asset 7/36) alt=
    Simple and to the point. I think this with an extended commentary section would be the best answer. – ooolala Jan 30 '16 at 5:05
  • 16
    There are memory implications that should be noted though. blog.jooq.org/2014/12/08/… – Amalgovinus Jun 9 '16 at 18:05
  • 1
    @Amalgovinus Basically, by creating a new subclass, you are hard-coding the type arguments from HashMap into this subclass. This can only work if you actually provide them. (With a new (empty) HashMap, the type arguments are not relevant.) – Paŭlo Ebermann Jul 12 '16 at 8:22
  • 1
    I like the cleanliness of it, but it creates unnecessary anonymous class and has the problems described here: c2.com/cgi/wiki?DoubleBraceInitialization – udachny Aug 25 '16 at 8:01
  • 1
    @hello_its_me: Because Its same as stackoverflow.com/a/6802512/1386911 answer, just the formatting different. And in this case this extended formatting has no additional value on top of compact format for readability. – Daniel Hári Oct 31 '16 at 21:51
  • 44

    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");
    
    answered Jul 4 '13 at 16:10
    Image (Asset 8/36) alt=
    I wrote an answer inspired by yours: stackoverflow.com/questions/507602/… – Gerold Broser Sep 11 '18 at 1:38
  • 1
    Another solution with Apache Utils that is never mentioned but is readable, using prior Java versions: MapUtils.putAll(new HashMap<String, String>(), new Object[] { "My key", "my value", ... – Rolintocour Oct 25 '18 at 6:41
  • 7

    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.

    answered Mar 30 '20 at 14:26
    Image (Asset 9/36) alt=

    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.

    answered Dec 17 '19 at 2:19
    Image (Asset 10/36) alt=

    If you need to place only one key-value pair, you can use Collections.singletonMap(key, value);

    answered May 9 '20 at 18:46
    Image (Asset 11/36) alt=