176

I need to initialize a constant HashMap and would like to do it in one line statement. Avoiding sth like this:

  hashMap.put("One", new Integer(1)); // adding value into HashMap
  hashMap.put("Two", new Integer(2));      
  hashMap.put("Three", new Integer(3));

similar to this in objective C:

[NSDictionary dictionaryWithObjectsAndKeys:
@"w",[NSNumber numberWithInt:1],
@"K",[NSNumber numberWithInt:2],
@"e",[NSNumber numberWithInt:4],
@"z",[NSNumber numberWithInt:5],
@"l",[NSNumber numberWithInt:6],
nil] 

I have not found any example that shows how to do this having looked at so many.

abhi
  • 1,760
  • 1
  • 24
  • 40
user387184
  • 10,953
  • 12
  • 77
  • 147

11 Answers11

323

You can use the Double Brace Initialization as shown below:

Map<String, Integer> hashMap = new HashMap<String, Integer>()
{{
     put("One", 1);
     put("Two", 2);
     put("Three", 3);
}};

As a piece of warning, please refer to the thread Efficiency of Java “Double Brace Initialization" for the performance implications that it might have.

Debargha Roy
  • 2,320
  • 1
  • 15
  • 34
Eng.Fouad
  • 115,165
  • 71
  • 313
  • 417
  • That looks interesting. Is there a name for that? It looks like the matrix operations in Excel... – user387184 Nov 24 '11 at 18:36
  • 12
    @user387184 Yeah, they call it "double brace initializer". See this topic: http://stackoverflow.com/questions/924285/efficiency-of-java-double-brace-initialization – Eng.Fouad Nov 24 '11 at 18:39
  • 2
    I just put it in my code and I get this warning/message in the line: "The serializable class does not declare a static final serialVersionUID field of type long". Can I just ignore that? what does this mean? Thanks – user387184 Nov 24 '11 at 18:44
  • 1
    @user387184 You can ignore it. See this topic: http://stackoverflow.com/questions/285793/why-should-i-bother-about-serialversionuid – Eng.Fouad Nov 24 '11 at 18:46
  • 43
    You should not use this method. It creates a new class for every time that you use it, which has **much** worse performance than just plainly creating a map. See http://stackoverflow.com/questions/924285/efficiency-of-java-double-brace-initialization – Timo Türschmann Apr 26 '16 at 13:59
  • 9
    The reason I downvoted this is because it didn't explain that this creates a new class for every time that you use it. I think that people should be aware of the tradeoffs of doing it this way. – idungotnosn Nov 08 '16 at 16:28
  • 9
    @TimoTürschmann Seems that if I ever needed static initialization of a map like this, that it would also be static, eliminating the *every time you use it* performance penalty - you'd have that penalty once. I can't see any other time that one would want this kind of initialization *without* the variable being static (e.g., would anyone ever use this in a loop?). I may be wrong though, programmers are inventive. – Chris Cirefice Nov 27 '16 at 23:41
  • 4
    Please don't use this anti-pattern, it's [actively dangerous](http://errorprone.info/bugpattern/DoubleBraceInitialization) and there are reasonable alternatives. – dimo414 Apr 03 '18 at 20:59
  • 1
    Another problem with this solution is that it can lead to memory leaks. Since it creates anonymoys class with reference to the instance of the owning object, leak can occur when anonymous inner class is returned and held by other objects. – Ariel Grabijas Apr 11 '19 at 10:48
86

Since Java 9, it is possible to use Map.of(...), like so:

Map<String, Integer> immutableMap = Map.of("One", 1, 
                                           "Two", 2, 
                                           "Three", 3);

This map is immutable. If you want the map to be mutable, you have to add:

Map<String, Integer> hashMap = new HashMap<>(immutableMap);

If you can't use Java 9, you're stuck with writing a similar helper method yourself or using a third-party library (like Guava) to add that functionality for you.

Timo Türschmann
  • 4,388
  • 1
  • 22
  • 30
  • 1
    After adding 10 entries, it throws strange error "can not resolve method", is this bug with this method ? – vikramvi Apr 02 '20 at 07:56
  • 5
    @vikramvi yes If you look at the [documentation](https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/util/Map.html#of(K,V,K,V,K,V,K,V,K,V,K,V,K,V,K,V,K,V,K,V)) `Map.of` is only done up to 10 entries since it is quite laborious – jolivier Jun 18 '20 at 14:45
73

You can use Google Guava's ImmutableMap. This works as long as you don't care about modifying the Map later (you can't call .put() on the map after constructing it using this method):

import com.google.common.collect.ImmutableMap;

// For up to five entries, use .of()
Map<String, Integer> littleMap = ImmutableMap.of(
    "One", Integer.valueOf(1),
    "Two", Integer.valueOf(2),
    "Three", Integer.valueOf(3)
);

// For more than five entries, use .builder()
Map<String, Integer> bigMap = ImmutableMap.<String, Integer>builder()
    .put("One", Integer.valueOf(1))
    .put("Two", Integer.valueOf(2))
    .put("Three", Integer.valueOf(3))
    .put("Four", Integer.valueOf(4))
    .put("Five", Integer.valueOf(5))
    .put("Six", Integer.valueOf(6))
    .build();

See also: http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/ImmutableMap.html

A somewhat related question: ImmutableMap.of() workaround for HashMap in Maps?

Community
  • 1
  • 1
JimmyT
  • 759
  • 5
  • 7
12

Maps have also had factory methods added in Java 9. For up to 10 entries Maps have overloaded constructors that take pairs of keys and values. For example we could build a map of various cities and their populations (according to google in October 2016) as follow:

Map<String, Integer> cities = Map.of("Brussels", 1_139000, "Cardiff", 341_000);

The var-args case for Map is a little bit harder, you need to have both keys and values, but in Java, methods can’t have two var-args parameters. So the general case is handled by taking a var-args method of Map.Entry<K, V> objects and adding a static entry() method that constructs them. For example:

Map<String, Integer> cities = Map.ofEntries(
    entry("Brussels", 1139000), 
    entry("Cardiff", 341000)
);

Collection Factory Methods in Java 9

Youcef LAIDANI
  • 55,661
  • 15
  • 90
  • 140
Sadiq Ali
  • 1,272
  • 2
  • 15
  • 22
8

Here's a simple class that will accomplish what you want

import java.util.HashMap;

public class QuickHash extends HashMap<String, String> {
    public QuickHash(String... KeyValuePairs) {
        super(KeyValuePairs.length/2);
        for(int i=0; i<KeyValuePairs.length; i+=2)
            put(KeyValuePairs[i], KeyValuePairs[i+1]);
    }
}

And then to use it

Map<String, String> Foo=new QuickHash(
    "a", "1",
    "b", "2"
);

This yields {a:1, b:2}

Dakusan
  • 6,504
  • 5
  • 32
  • 45
8

Java has no map literal, so there's no nice way to do exactly what you're asking.

If you need that type of syntax, consider some Groovy, which is Java-compatible and lets you do:

def map = [name:"Gromit", likes:"cheese", id:1234]
dfraser
  • 301
  • 2
  • 7
4
    boolean x;
    for (x = false, 
        map.put("One", new Integer(1)), 
        map.put("Two", new Integer(2)),      
        map.put("Three", new Integer(3)); x;);

Ignoring the declaration of x (which is necessary to avoid an "unreachable statement" diagnostic), technically it's only one statement.

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
2

You could add this utility function to a utility class:

public static <K, V> Map<K, V> mapOf(Object... keyValues) {
    Map<K, V> map = new HashMap<>(keyValues.length / 2);

    for (int index = 0; index < keyValues.length / 2; index++) {
        map.put((K)keyValues[index * 2], (V)keyValues[index * 2 + 1]);
    }

    return map;
}

Map<Integer, String> map1 = YourClass.mapOf(1, "value1", 2, "value2");
Map<String, String> map2 = YourClass.mapOf("key1", "value1", "key2", "value2");

Note: in Java 9 you can use Map.of

R. Oosterholt
  • 7,720
  • 2
  • 53
  • 77
  • This method shouldn't be used since the implementation is not type safe! Example: Map map1 = mapOf(1, "value1", "key", 2, 2L); – juls Aug 10 '20 at 09:54
  • Well, I expect the programmer which defines `Map`, to also supply those types... – R. Oosterholt Aug 12 '20 at 06:55
0

Based on solution, presented by @Dakusan (the class defining to extend the HashMap), I did it this way:

  public static HashMap<String,String> SetHash(String...pairs) {
     HashMap<String,String> rtn = new HashMap<String,String>(pairs.length/2);
     for ( int n=0; n < pairs.length; n+=2 ) rtn.put(pairs[n], pairs[n + 1]);
    return rtn; 
  }

.. and using it this way:

HashMap<String,String> hm = SetHash( "one","aa", "two","bb", "tree","cc");

(Not sure if there is any disadvantages in that way (I am not a java developer, just has to do some task in java), but it works and seems to me comfortable.)

alex5161
  • 19
  • 5
0

We can use JSON to achieve this, like following using Gson

Map<String,Integer> hashMap = new Gson().fromJson("{'one':1,'two':2,'three':3}",HashMap.class);

Or using Jackson

Map<String,Integer> hashMap = new ObjectMapper().readValue("{'one':1,'two':2,'three':3}",HashMap.class);
Shuyou
  • 81
  • 1
  • 5
-1

Another approach may be writing special function to extract all elements values from one string by regular-expression:

import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Example {
    public static void main (String[] args){
        HashMap<String,Integer> hashMapStringInteger = createHashMapStringIntegerInOneStat("'one' => '1', 'two' => '2' , 'three'=>'3'  ");

        System.out.println(hashMapStringInteger); // {one=1, two=2, three=3}
    }

    private static HashMap<String, Integer> createHashMapStringIntegerInOneStat(String str) {
        HashMap<String, Integer> returnVar = new HashMap<String, Integer>();

        String currentStr = str;
        Pattern pattern1 = Pattern.compile("^\\s*'([^']*)'\\s*=\\s*>\\s*'([^']*)'\\s*,?\\s*(.*)$");

        // Parse all elements in the given string.
        boolean thereIsMore = true;
        while (thereIsMore){
            Matcher matcher = pattern1.matcher(currentStr);
            if (matcher.find()) {
                returnVar.put(matcher.group(1),Integer.valueOf(matcher.group(2)));
                currentStr = matcher.group(3);
            }
            else{
                thereIsMore = false;
            }
        }

        // Validate that all elements in the given string were parsed properly
        if (currentStr.length() > 0){
            System.out.println("WARNING: Problematic string format. given String: " + str);
        }

        return returnVar;
    }
}
Uri Ziv
  • 77
  • 3