ChatGPT解决这个技术问题 Extra ChatGPT

How can I initialise a static Map?

How would you initialise a static Map in Java?

Method one: static initialiser Method two: instance initialiser (anonymous subclass) or some other method?

What are the pros and cons of each?

Here is an example illustrating the two methods:

import java.util.HashMap;
import java.util.Map;

public class Test {
    private static final Map<Integer, String> myMap = new HashMap<>();
    static {
        myMap.put(1, "one");
        myMap.put(2, "two");
    }

    private static final Map<Integer, String> myMap2 = new HashMap<>(){
        {
            put(1, "one");
            put(2, "two");
        }
    };
}
For initializing a map in Java 8: stackoverflow.com/a/37384773/1216775
Please, never use double brace initialization - it's a hack, and an easy way to leak memory and cause other problems.
Java 9? If entries count <= 10 use Map.of else Map.ofEntries, check stackoverflow.com/a/37384773/1216775

6
6 revs, 5 users 85%

The instance initialiser is just syntactic sugar in this case, right? I don't see why you need an extra anonymous class just to initialize. And it won't work if the class being created is final.

You can create an immutable map using a static initialiser too:

public class Test {
    private static final Map<Integer, String> myMap;
    static {
        Map<Integer, String> aMap = ....;
        aMap.put(1, "one");
        aMap.put(2, "two");
        myMap = Collections.unmodifiableMap(aMap);
    }
}

This is the idiom I've used for years and I've never had anyone bat an eye at it. I do the same for unmodifiable constant Sets and Lists too.
How would I handle a HashMap with a String key. The Map object doesn't allow me to have a String key so I can't use unmodifiableMap(). I guess casting to a HashMap would defeat the purpose as well. Any ideas?
@Luke I seriously doubt that Android has such a limitation. It makes no sense at all. A quick search found this question here (and many others) which seems to imply you can use a String key for a Map object in Android.
So no one else bothers to investigate, I can confirm there's no problem with using a String key for a Map object on Android.
Jordan: it is an old topic now but I suspect @Luke was trying to use a string as a key in a map that had a different key type, e.g. Map.
J
Jonik

I like the Guava way of initialising a static, immutable map:

static final Map<Integer, String> MY_MAP = ImmutableMap.of(
    1, "one",
    2, "two"
);

As you can see, it's very concise (because of the convenient factory methods in ImmutableMap).

If you want the map to have more than 5 entries, you can no longer use ImmutableMap.of(). Instead, try ImmutableMap.builder() along these lines:

static final Map<Integer, String> MY_MAP = ImmutableMap.<Integer, String>builder()
    .put(1, "one")
    .put(2, "two")
    // ... 
    .put(15, "fifteen")
    .build();

To learn more about the benefits of Guava's immutable collection utilities, see Immutable Collections Explained in Guava User Guide.

(A subset of) Guava used to be called Google Collections. If you aren't using this library in your Java project yet, I strongly recommend trying it out! Guava has quickly become one of the most popular and useful free 3rd party libs for Java, as fellow SO users agree. (If you are new to it, there are some excellent learning resources behind that link.)

Update (2015): As for Java 8, well, I would still use the Guava approach because it is way cleaner than anything else. If you don't want Guava dependency, consider a plain old init method. The hack with two-dimensional array and Stream API is pretty ugly if you ask me, and gets uglier if you need to create a Map whose keys and values are not the same type (like Map<Integer, String> in the question).

As for future of Guava in general, with regards to Java 8, Louis Wasserman said this back in 2014, and [update] in 2016 it was announced that Guava 21 will require and properly support Java 8.

Update (2016): As Tagir Valeev points out, Java 9 will finally make this clean to do using nothing but pure JDK, by adding convenience factory methods for collections:

static final Map<Integer, String> MY_MAP = Map.of(
    1, "one", 
    2, "two"
);

Seems like our fellow SO admins have deleted the venerable "Most useful free third party Java libraries" question to which I linked. :( Damn them.
I agree, this is the nicest way of initializing a constant map. Not only more readable but also since Collections.unmodifiableMap returns a read-only view of the underlying map (that still can be modified).
I can now see deleted questions (with 10k+ rep), so here's a copy of 'Most useful free third-party Java libraries'. It's only the first page, but at least you can find the Guava resources mentioned above.
I really prefer this approach, although it's beneficial to know how to do it without extra dependencies.
JEP 186 still not closed, so it may introduce new features related to collection literals
B
Bouramas

I would use:

public class Test {
    private static final Map<Integer, String> MY_MAP = createMap();

    private static Map<Integer, String> createMap() {
        Map<Integer, String> result = new HashMap<>();
        result.put(1, "one");
        result.put(2, "two");
        return Collections.unmodifiableMap(result);
    }
}

it avoids an anonymous class, which I personally consider to be a bad style, and avoid it makes the creation of map more explicit it makes map unmodifiable as MY_MAP is constant, I would name it like constant


Of the pure JDK options (no libs), I like this the most, because map definition is clearly linked to its initialisation. Also agreed on constant naming.
It never occurred to me that you could do this.
C
Chris Noe

Java 5 provides this more compact syntax:

static final Map<String , String> FLAVORS = new HashMap<String , String>() {{
    put("Up",    "Down");
    put("Charm", "Strange");
    put("Top",   "Bottom");
}};

That technique is called double brace initialization: stackoverflow.com/questions/1372113/… It's not a special Java 5 syntax, it's just a trick with an anonymous class with an instance initializer.
Quick question regarding the double brace initialization: When doing this, Eclipse issues a Warning about a missing Serial ID. On one hand, I don't see why a Serial ID would be needed in this specific case, but on the other hand, I usually don't like supressing warnings. What are your thoughts on this?
@nbarraille That's because HashMap implements Serializable. Since you actually create a subclass of HashMap using this "trick", you implicitly creating a Serializable class. And for this you should supply a serialUID.
Double brace initialization can cause memory leaks when used from a non-static context, because the anonymous class created will maintain a reference to the surrounding object. It has worse performance than regular initialization because of the additional class loading required. It can cause equals() comparisons to fail, if the equals() method does not accept subclasses as parameter. And finally, pre Java 9 it cannot be combined with the diamond operator, because that cannot be used with anonymous classes. – IntelliJ
@MarkJeronimus - The suggested use is a static context. The performance may be worse, but not noticeably so when dealing with a presumably small number of statically defined maps. HashMap.equals is defined in AbstractMap and works on any subclass of Map, so that isn't a concern here. The diamond operator thing is annoying, but as mentioned has now been resolved.
M
Michael Lihs

One advantage to the second method is that you can wrap it with Collections.unmodifiableMap() to guarantee that nothing is going to update the collection later:

private static final Map<Integer, String> CONSTANT_MAP = 
    Collections.unmodifiableMap(new HashMap<Integer, String>() {{ 
        put(1, "one");
        put(2, "two");
    }});

 // later on...

 CONSTANT_MAP.put(3, "three"); // going to throw an exception!

Can't you easily do this in the first method by moving the new operator into the static {} block and wrapping it?
I'd move the constructor call into the static initialised anyway. Anything else just looks odd.
any idea what performance hit there might be from using an anonymous class as opposed to a concrete class?
B
Basil Bourque

Map.of in Java 9+

private static final Map<Integer, String> MY_MAP = Map.of(1, "one", 2, "two");

See JEP 269 for details. JDK 9 reached general availability in September 2017.


Or if you want more than 10 key-value pairs, you can use Map.ofEntries
This is clean and all, until you realize how it was implemented
Ugh thats so sad - looks like it only supports 10 entries, after which you need to use ofEntries. Lame.
The implementation cleanliness in the JDK shouldn't matter as long as it works and satisfies the contract. Like any black box, implementation details can always be fixed in future if really needed...
@mid That's the only typesafe way to do this in Java.
L
Luke Hutchison

Here's a Java 8 one-line static map initializer:

private static final Map<String, String> EXTENSION_TO_MIMETYPE =
    Arrays.stream(new String[][] {
        { "txt", "text/plain" }, 
        { "html", "text/html" }, 
        { "js", "application/javascript" },
        { "css", "text/css" },
        { "xml", "application/xml" },
        { "png", "image/png" }, 
        { "gif", "image/gif" }, 
        { "jpg", "image/jpeg" },
        { "jpeg", "image/jpeg" }, 
        { "svg", "image/svg+xml" },
    }).collect(Collectors.toMap(kv -> kv[0], kv -> kv[1]));

Edit: to initialize a Map<Integer, String> as in the question, you'd need something like this:

static final Map<Integer, String> MY_MAP = Arrays.stream(new Object[][]{
        {1, "one"},
        {2, "two"},
}).collect(Collectors.toMap(kv -> (Integer) kv[0], kv -> (String) kv[1]));

Edit(2): There is a better, mixed-type-capable version by i_am_zero that uses a stream of new SimpleEntry<>(k, v) calls. Check out that answer: https://stackoverflow.com/a/37384773/3950982


I took the liberty to add a version that is equivalent with the question and other answers: init a Map whose keys and values are of different type (so String[][] won't do, Object[][] is needed). IMHO, this approach is ugly (even more so with the casts) and hard to remember; wouldn't use it myself.
a
akhil_mittal

Java 9

We can use Map.ofEntries, calling Map.entry( k , v ) to create each entry.

import static java.util.Map.entry;
private static final Map<Integer,String> map = Map.ofEntries(
        entry(1, "one"),
        entry(2, "two"),
        entry(3, "three"),
        entry(4, "four"),
        entry(5, "five"),
        entry(6, "six"),
        entry(7, "seven"),
        entry(8, "eight"),
        entry(9, "nine"),
        entry(10, "ten"));

We can also use Map.of as suggested by Tagir in his answer here but we cannot have more than 10 entries using Map.of.

Java 8

We can create a Stream of map entries. We already have two implementations of Entry in java.util.AbstractMap which are SimpleEntry and SimpleImmutableEntry. For this example we can make use of former as:

import java.util.AbstractMap.*;
private static final Map<Integer, String> myMap = Stream.of(
            new SimpleEntry<>(1, "one"),
            new SimpleEntry<>(2, "two"),
            new SimpleEntry<>(3, "three"),
            new SimpleEntry<>(4, "four"),
            new SimpleEntry<>(5, "five"),
            new SimpleEntry<>(6, "six"),
            new SimpleEntry<>(7, "seven"),
            new SimpleEntry<>(8, "eight"),
            new SimpleEntry<>(9, "nine"),
            new SimpleEntry<>(10, "ten"))
            .collect(Collectors.toMap(SimpleEntry::getKey, SimpleEntry::getValue));
            

The new SimpleEntry<>() way is far less readable than static put() :/
D
Donald Raab

With Eclipse Collections, all of the following will work:

import java.util.Map;

import org.eclipse.collections.api.map.ImmutableMap;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.impl.factory.Maps;

public class StaticMapsTest
{
    private static final Map<Integer, String> MAP =
        Maps.mutable.with(1, "one", 2, "two");

    private static final MutableMap<Integer, String> MUTABLE_MAP =
       Maps.mutable.with(1, "one", 2, "two");


    private static final MutableMap<Integer, String> UNMODIFIABLE_MAP =
        Maps.mutable.with(1, "one", 2, "two").asUnmodifiable();


    private static final MutableMap<Integer, String> SYNCHRONIZED_MAP =
        Maps.mutable.with(1, "one", 2, "two").asSynchronized();


    private static final ImmutableMap<Integer, String> IMMUTABLE_MAP =
        Maps.mutable.with(1, "one", 2, "two").toImmutable();


    private static final ImmutableMap<Integer, String> IMMUTABLE_MAP2 =
        Maps.immutable.with(1, "one", 2, "two");
}

You can also statically initialize primitive maps with Eclipse Collections.

import org.eclipse.collections.api.map.primitive.ImmutableIntObjectMap;
import org.eclipse.collections.api.map.primitive.MutableIntObjectMap;
import org.eclipse.collections.impl.factory.primitive.IntObjectMaps;

public class StaticPrimitiveMapsTest
{
    private static final MutableIntObjectMap<String> MUTABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two");

    private static final MutableIntObjectMap<String> UNMODIFIABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .asUnmodifiable();

    private static final MutableIntObjectMap<String> SYNCHRONIZED_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .asSynchronized();

    private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP =
            IntObjectMaps.mutable.<String>empty()
                    .withKeyValue(1, "one")
                    .withKeyValue(2, "two")
                    .toImmutable();

    private static final ImmutableIntObjectMap<String> IMMUTABLE_INT_OBJ_MAP2 =
            IntObjectMaps.immutable.<String>empty()
                    .newWithKeyValue(1, "one")
                    .newWithKeyValue(2, "two");
} 

Note: I am a committer for Eclipse Collections


I really wish Eclipse Collections was the default collections library for Java. I enjoy it so much more than Guava + JCL.
e
eljenso

I would never create an anonymous subclass in this situation. Static initializers work equally well, if you would like to make the map unmodifiable for example:

private static final Map<Integer, String> MY_MAP;
static
{
    Map<Integer, String>tempMap = new HashMap<Integer, String>();
    tempMap.put(1, "one");
    tempMap.put(2, "two");
    MY_MAP = Collections.unmodifiableMap(tempMap);
}

In which situation would you use an anonymous subclass to initialise a hashmap then?
Never to initialize a Collection.
Could you explain why using a static initializer is a better choice than creating an anonymous subclass?
@rookie There are several reasons given in other answers favoring the static init. The goal here is to initialize, so why bring in the subclassing, except maybe to save a few keystrokes? (If you want to save on keystrokes, Java is definitely not a good choice as a programming language.) One rule of thumb I use when programming in Java is: subclass as little as possible (and never when it can be reasonably avoided).
@eljenso - the reason I generally favour the subclass syntax for this is that it puts the initialisation inline, where it belongs. A second-best choice is to call a static method that returns the initialised map. But I'm afraid I'd look at your code and have to spend a few seconds working out where MY_MAP comes from, and that's time that I don't want to have to waste. Any improvement to readability is a bonus, and the performance consequences are minimal, so it seems like the best option to me.
P
Paŭlo Ebermann

Maybe it's interesting to check out Google Collections, e.g. the videos that they have on their page. They provide various ways to initialize maps and sets, and provide immutable collections as well.

Update: This library is now named Guava.


p
peterh

I like anonymous class, because it is easy to deal with it:

public static final Map<?, ?> numbers = Collections.unmodifiableMap(new HashMap<Integer, String>() {
    {
        put(1, "some value");
                    //rest of code here
    }
});

L
Leninkumar Koppoju
public class Test {
    private static final Map<Integer, String> myMap;
    static {
        Map<Integer, String> aMap = ....;
        aMap.put(1, "one");
        aMap.put(2, "two");
        myMap = Collections.unmodifiableMap(aMap);
    }
}

If we declare more than one constant then that code will be written in static block and that is hard to maintain in future. So it is better to use anonymous class.

public class Test {

    public static final Map numbers = Collections.unmodifiableMap(new HashMap(2, 1.0f){
        {
            put(1, "one");
            put(2, "two");
        }
    });
}

And it is suggested to used unmodifiableMap for constants other wise it can't be treated as constant.


D
Dennis C

I could strongly suggest the "double brace initialization" style over static block style.

Someone may comment that they don't like anonymous class, overhead, performance, etc.

But that I more consider is the code readability and maintainability. In this point of view, I stand a double brace is a better code style rather then static method.

The elements are nested and inline. It is more OO, not procedural. the performance impact is really small and could be ignored. Better IDE outline support (rather then many anonymous static{} block) You saved few lines of comment to bring them relationship. Prevent possible element leak/instance lead of uninitialized object from exception and bytecode optimizer. No worry about the order of execution of static block.

In addition, it you aware the GC of the anonymous class, you can always convert it to a normal HashMap by using new HashMap(Map map).

You can do this until you faced another problem. If you do, you should use complete another coding style (e.g. no static, factory class) for it.


a
agad

As usual apache-commons has proper method MapUtils.putAll(Map, Object[]):

For example, to create a color map:

Map<String, String> colorMap = MapUtils.putAll(new HashMap<String, String>(), new String[][] {
     {"RED", "#FF0000"},
     {"GREEN", "#00FF00"},
     {"BLUE", "#0000FF"}
 });

I include Apache Commons in all builds so, in the unfortunate absence of a method Arrays.asMap( ... ) in plain Java, I think this is the best solution. Reinventing the wheel is usually silly. Very slight downside is that with generics it will need an unchecked conversion.
@mikerodent 4.1 version is generic: public static Map putAll(final Map map, final Object[] array)
Tx ... yes, I'm using 4.1 but I still have to SuppressWarnings( unchecked ) in Eclipse with a line like Map<String, String> dummy = MapUtils.putAll(new HashMap<String, String>(), new Object[][]... )
@mikerodent isn't it because of Object[][]? See updated unswear - I don't have any warning in Eclipse.
How strange... even when I go String[][] I get the "warning"! And of course that only works if your K and V are the same class. I take it you haven't (understandably) set "unchecked conversion" to "Ignore" in your Eclipse setup?
n
neu242

Here's my favorite if I

don't want to (or cannot) use Guava's ImmutableMap.of()

or I need a mutable Map

or I need more than the 10 entry limit in Map.of() from JDK9+

public static <A> Map<String, A> asMap(Object... keysAndValues) {
  return new LinkedHashMap<String, A>() {{
    for (int i = 0; i < keysAndValues.length - 1; i++) {
      put(keysAndValues[i].toString(), (A) keysAndValues[++i]);
    }
  }};
}

It's very compact, and it ignores stray values (i.e. a final key without a value).

Usage:

Map<String, String> one = asMap("1stKey", "1stVal", "2ndKey", "2ndVal");
Map<String, Object> two = asMap("1stKey", Boolean.TRUE, "2ndKey", new Integer(2));

i
icza

I prefer using a static initializer to avoid generating anonymous classes (which would have no further purpose), so I'll list tips initializing with a static initializer. All listed solutions / tips are type-safe.

Note: The question doesn't say anything about making the map unmodifiable, so I will leave that out, but know that it can easily be done with Collections.unmodifiableMap(map).

First tip

The 1st tip is that you can make a local reference to the map and you give it a SHORT name:

private static final Map<Integer, String> myMap = new HashMap<>();
static {
    final Map<Integer, String> m = myMap; // Use short name!
    m.put(1, "one"); // Here referencing the local variable which is also faster!
    m.put(2, "two");
    m.put(3, "three");
}

Second tip

The 2nd tip is that you can create a helper method to add entries; you can also make this helper method public if you want to:

private static final Map<Integer, String> myMap2 = new HashMap<>();
static {
    p(1, "one"); // Calling the helper method.
    p(2, "two");
    p(3, "three");
}

private static void p(Integer k, String v) {
    myMap2.put(k, v);
}

The helper method here is not re-usable though because it can only add elements to myMap2. To make it re-usable, we could make the map itself a parameter of the helper method, but then initialization code would not be any shorter.

Third tip

The 3rd tip is that you can create a re-usable builder-like helper class with the populating functionality. This is really a simple, 10-line helper class which is type-safe:

public class Test {
    private static final Map<Integer, String> myMap3 = new HashMap<>();
    static {
        new B<>(myMap3)   // Instantiating the helper class with our map
            .p(1, "one")
            .p(2, "two")
            .p(3, "three");
    }
}

class B<K, V> {
    private final Map<K, V> m;

    public B(Map<K, V> m) {
        this.m = m;
    }

    public B<K, V> p(K k, V v) {
        m.put(k, v);
        return this; // Return this for chaining
    }
}

B
Bharanidharan K

If you want unmodifiable map, finally java 9 added a cool factory method of to Map interface. Similar method is added to Set, List as well.

Map<String, String> unmodifiableMap = Map.of("key1", "value1", "key2", "value2");


B
Brian Agnew

The anonymous class you're creating works well. However you should be aware that this is an inner class and as such, it'll contain a reference to the surrounding class instance. So you'll find you can't do certain things with it (using XStream for one). You'll get some very strange errors.

Having said that, so long as you're aware then this approach is fine. I use it most of the time for initialising all sorts of collections in a concise fashion.

EDIT: Pointed out correctly in the comments that this is a static class. Obviously I didn't read this closely enough. However my comments do still apply to anonymous inner classes.


In this particular case it's static, so no outer instance.
Arguably XStream shouldn't be trying to serialize stuff like this (it's static. Why would you need to serialize a static variable?)
P
Philip Guin

If you want something terse and relatively safe, you can just shift compile-time type checking to run-time:

static final Map<String, Integer> map = MapUtils.unmodifiableMap(
    String.class, Integer.class,
    "cat",  4,
    "dog",  2,
    "frog", 17
);

This implementation should catch any errors:

import java.util.HashMap;

public abstract class MapUtils
{
    private MapUtils() { }

    public static <K, V> HashMap<K, V> unmodifiableMap(
            Class<? extends K> keyClazz,
            Class<? extends V> valClazz,
            Object...keyValues)
    {
        return Collections.<K, V>unmodifiableMap(makeMap(
            keyClazz,
            valClazz,
            keyValues));
    }

    public static <K, V> HashMap<K, V> makeMap(
            Class<? extends K> keyClazz,
            Class<? extends V> valClazz,
            Object...keyValues)
    {
        if (keyValues.length % 2 != 0)
        {
            throw new IllegalArgumentException(
                    "'keyValues' was formatted incorrectly!  "
                  + "(Expected an even length, but found '" + keyValues.length + "')");
        }

        HashMap<K, V> result = new HashMap<K, V>(keyValues.length / 2);

        for (int i = 0; i < keyValues.length;)
        {
            K key = cast(keyClazz, keyValues[i], i);
            ++i;
            V val = cast(valClazz, keyValues[i], i);
            ++i;
            result.put(key, val);
        }

        return result;
    }

    private static <T> T cast(Class<? extends T> clazz, Object object, int i)
    {
        try
        {
            return clazz.cast(object);
        }
        catch (ClassCastException e)
        {
            String objectName = (i % 2 == 0) ? "Key" : "Value";
            String format = "%s at index %d ('%s') wasn't assignable to type '%s'";
            throw new IllegalArgumentException(String.format(format, objectName, i, object.toString(), clazz.getSimpleName()), e);
        }
    }
}

z
zrvan

With Java 8 I've come to use the following pattern:

private static final Map<String, Integer> MAP = Stream.of(
    new AbstractMap.SimpleImmutableEntry<>("key1", 1),
    new AbstractMap.SimpleImmutableEntry<>("key2", 2)
).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

It's not the most terse and a bit roundabout, but

it doesn't require anything outside of java.util

it's typesafe and easily accommodates different types for key and value.


if needed, one can use the toMap signature including a map supplier to specify the type of map.
S
Stromata

If you only need to add one value to the map you can use Collections.singletonMap:

Map<K, V> map = Collections.singletonMap(key, value)

y
yegor256

You may use StickyMap and MapEntry from Cactoos:

private static final Map<String, String> MAP = new StickyMap<>(
  new MapEntry<>("name", "Jeffrey"),
  new MapEntry<>("age", "35")
);

R
R. Oosterholt

Your second approach (Double Brace initialization) is thought to be an anti pattern, so I would go for the first approach.

Another easy way to initialise a static Map is by using this utility function:

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 = mapOf(1, "value1", 2, "value2");
Map<String, String> map2 = mapOf("key1", "value1", "key2", "value2");

Note: in Java 9 you can use Map.of


S
Stanley Borowy

I do not like Static initializer syntax and I'm not convinced to anonymous subclasses. Generally, I agree with all cons of using Static initializers and all cons of using anonymous subclasses that were mentioned in previus answers. On the other hand - pros presented in these posts are not enough for me. I prefer to use static initialization method:

public class MyClass {
    private static final Map<Integer, String> myMap = prepareMap();

    private static Map<Integer, String> prepareMap() {
        Map<Integer, String> hashMap = new HashMap<>();
        hashMap.put(1, "one");
        hashMap.put(2, "two");

        return hashMap;
    }
}

j
josh

I have not seen the approach I use (and have grown to like) posted in any answers, so here it is:

I don't like using static initializers because they are clunky, and I don't like anonymous classes because it is creating a new class for each instance.

instead, I prefer initialization that looks like this:

map(
    entry("keyA", "val1"),
    entry("keyB", "val2"),
    entry("keyC", "val3")
);

unfortunately, these methods are not part of the standard Java library, so you will need to create (or use) a utility library that defines the following methods:

 public static <K,V> Map<K,V> map(Map.Entry<K, ? extends V>... entries)
 public static <K,V> Map.Entry<K,V> entry(K key, V val)

(you can use 'import static' to avoid needing to prefix the method's name)

I found it useful to provide similar static methods for the other collections (list, set, sortedSet, sortedMap, etc.)

Its not quite as nice as json object initialization, but it's a step in that direction, as far as readability is concerned.


c
catch23

Because Java does not support map literals, map instances must always be explicitly instantiated and populated.

Fortunately, it is possible to approximate the behavior of map literals in Java using factory methods.

For example:

public class LiteralMapFactory {

    // Creates a map from a list of entries
    @SafeVarargs
    public static <K, V> Map<K, V> mapOf(Map.Entry<K, V>... entries) {
        LinkedHashMap<K, V> map = new LinkedHashMap<>();
        for (Map.Entry<K, V> entry : entries) {
            map.put(entry.getKey(), entry.getValue());
        }
        return map;
    }
    // Creates a map entry
    public static <K, V> Map.Entry<K, V> entry(K key, V value) {
        return new AbstractMap.SimpleEntry<>(key, value);
    }

    public static void main(String[] args) {
        System.out.println(mapOf(entry("a", 1), entry("b", 2), entry("c", 3)));
    }
}

Output:

{a=1, b=2, c=3}

It is a lot more convenient than creating and populating the map an element at a time.


A
Ali Dehghani

JEP 269 provides some convenience factory methods for Collections API. This factory methods are not in current Java version, which is 8, but are planned for Java 9 release.

For Map there are two factory methods: of and ofEntries. Using of, you can pass alternating key/value pairs. For example, in order to create a Map like {age: 27, major: cs}:

Map<String, Object> info = Map.of("age", 27, "major", "cs");

Currently there are ten overloaded versions for of, so you can create a map containing ten key/value pairs. If you don't like this limitation or alternating key/values, you can use ofEntries:

Map<String, Object> info = Map.ofEntries(
                Map.entry("age", 27),
                Map.entry("major", "cs")
);

Both of and ofEntries will return an immutable Map, so you can't change their elements after construction. You can try out these features using JDK 9 Early Access.


j
jglatre

Well... I like enums ;)

enum MyEnum {
    ONE   (1, "one"),
    TWO   (2, "two"),
    THREE (3, "three");

    int value;
    String name;

    MyEnum(int value, String name) {
        this.value = value;
        this.name = name;
    }

    static final Map<Integer, String> MAP = Stream.of( values() )
            .collect( Collectors.toMap( e -> e.value, e -> e.name ) );
}

V
Vlasec

I've read the answers and i decided to write my own map builder. Feel free to copy-paste and enjoy.

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * A tool for easy creation of a map. Code example:<br/>
 * {@code MapBuilder.of("name", "Forrest").and("surname", "Gump").build()}
 * @param <K> key type (inferred by constructor)
 * @param <V> value type (inferred by constructor)
 * @author Vlasec (for http://stackoverflow.com/a/30345279/1977151)
 */
public class MapBuilder <K, V> {
    private Map<K, V> map = new HashMap<>();

    /** Constructor that also enters the first entry. */
    private MapBuilder(K key, V value) {
        and(key, value);
    }

    /** Factory method that creates the builder and enters the first entry. */
    public static <A, B> MapBuilder<A, B> mapOf(A key, B value) {
        return new MapBuilder<>(key, value);
    }

    /** Puts the key-value pair to the map and returns itself for method chaining */
    public MapBuilder<K, V> and(K key, V value) {
        map.put(key, value);
        return this;
    }

    /**
     * If no reference to builder is kept and both the key and value types are immutable,
     * the resulting map is immutable.
     * @return contents of MapBuilder as an unmodifiable map.
     */
    public Map<K, V> build() {
        return Collections.unmodifiableMap(map);
    }
}

EDIT: Lately, I keep finding public static method of pretty often and I kinda like it. I added it into the code and made the constructor private, thus switching to static factory method pattern.

EDIT2: Even more recently, I no longer like static method called of, as it looks pretty bad when using static imports. I renamed it to mapOf instead, making it more suitable for static imports.