开发者

Is there a best practice for writing maps literal style in Java?

In short, if you want to write a map of e.g. constants in Java, which in e.g. Python and Javascript y开发者_StackOverflowou would write as a literal,

T<String,String> CONSTANTS =
{
    "CONSTANT_NAME_0": CONSTANT_VALUE_0 ,
    "CONSTANT_NAME_1": CONSTANT_VALUE_1 ,
    "CONSTANT_NAME_2": CONSTANT_VALUE_2 ,
    //...
} ;

is there a Class or any preset Object that you can use for writing a data structure like that?


I like to do it this way:

Map map = new HashMap() {{
    put("foo", "bar");
    put(123, 456);
}};

The double {{ }} are an instance initialization block. They are a bit unusual but they are useful. No need for libraries or helpers.


No, Java doesn't have a map literal. The closest you'll come to this is using Google Collections' ImmutableMap:

Map<K,V> CONSTANTS = ImmutableMap.of(
    NAME_1, VALUE_1,
    NAME_2, VALUE_2
    //etc.
  );


Constants? I'd use an enum.

public enum Constants { 
    NAME_1("Value1"),
    NAME_2("Value2"),
    NAME_3("Value3");

    private String value;

    Constants(String value) {
        this.value = value;
    }

    public String value() {
        return value;
    }
}

Value for e.g. NAME_2 can be obtained as follows:

String name2value = Constants.NAME_2.value();

Only give the enum a bit more sensible name, e.g. Settings, Defaults, etc, whatever those name/value pairs actually represent.


Sorry, I'm a tinkerer :-) Here's a somewhat cleaner way.

public class MapTest {
    private static Map<String, String> map;

    static {
        Map<String, String> tmpMap = new HashMap<String, String>();

        tmpMap.put("A", "Apple");
        tmpMap.put("B", "Banana");
        // etc
        map = Collections.unmodifiableMap(tmpMap);
    }

    public Map<String, String> getMap() {
        return map;
    }
}


You can write yourself a quick helper function:

@SuppressWarnings("unchecked")
public static <K,V> Map<K,V> ImmutableMap(Object... keyValPair){
    Map<K,V> map = new HashMap<K,V>();

    if(keyValPair.length % 2 != 0){
        throw new IllegalArgumentException("Keys and values must be pairs.");
    }

    for(int i = 0; i < keyValPair.length; i += 2){
        map.put((K) keyValPair[i], (V) keyValPair[i+1]);
    }

    return Collections.unmodifiableMap(map);
}

Note the code above isn't going to stop you from overwriting constants of the same name, using CONST_1 multiple places in your list will result in the final CONST_1's value appearing.

Usage is something like:

Map<String,Double> constants = ImmutableMap(
    "CONST_0", 1.0,
    "CONST_1", 2.0
);


Here's another way, best suited for maps that won't be changing:

public class Whatever {
    private static Map<String,String> map = new HashMap<String,String>();

    static {
        map.put("A", "Apple");
        map.put("B", "Banana");
        // etc
    }
}


Java7 suppose to implement following syntax:

Map<String, String> = {
    "key1": "value",
    "key2": "value",
    "key3": "value",
    "key4": "value"
};

However now you're forced to use solutions proposed by Jorn or Tony Ennis.


Ok, with Jorn's improvement I can't seem to change the map at all, internally or externally. Perhaps not quite as readable, but if you need the map to be unmodifiable I think this is better.

public class MapTest {
    private static Map<String, String> map = initMap();

    private static Map<String, String> initMap() {
        Map<String, String> map = new HashMap<String, String>();

        map.put("A", "Apple");
        map.put("B", "Banana");
        // etc
        return Collections.unmodifiableMap(map);
    }

    public Map<String, String> getMap() {
        return map;
    }

    public static void main(String[] args) {
        MapTest m = new MapTest();
        System.out.println(m.getMap().get("A"));
        m.getMap().put("this", "that");
    }
}


I like to do the declaration and initialization on the same line. I've used this handy little utility for so long it basically is my "map literal" and until they're done "right" in the languange, I'm gonna continue on using it like that :)

Happy to share it here.

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

/**
 * A handy utility for creating and initializing Maps in a single statement.
 * @author Jonathan Cobb. This source code is in the Public Domain.
 */
public class MapBuilder {

    /**
     * Most common create/init case. Usage:
     *
     *   Map<String, Boolean> myPremadeMap = MapBuilder.build(new Object[][]{
     *     { "a", true }, { "b", false }, { "c", true }, { "d", true },
     *     { "e", "yes, still dangerous but at least it's not an anonymous class" }
     *   });
     *
     * If your keys and values are of the same type, it will even be typesafe:
     *   Map<String, String> someProperties = MapBuilder.build(new String[][]{
     *       {"propA", "valueA" }, { "propB", "valueB" }
     *   });
     *
     * @param values [x][2] array. items at [x][0] are keys and [x][1] are values.
     * @return a LinkedHashMap (to preserve order of declaration) with the "values" mappings
     */
    public static <K,V> Map<K,V> build(Object[][] values) {
        return build(new LinkedHashMap<K,V>(), values);
    }

    /**
     * Usage:
     *  Map<K,V> myMap = MapBuilder.build(new MyMapClass(options),
     *                                    new Object[][]{ {k,v}, {k,v}, ... });
     * @param map add key/value pairs to this map
     * @return the map passed in, now containing new "values" mappings
     */
    public static <K,V> Map<K,V> build(Map<K,V> map, Object[][] values) {
        for (Object[] value : values) {
            map.put((K) value[0], (V) value[1]);
        }
        return map;
    }

    /** Same as above, for single-value maps */
    public static <K,V> Map<K,V> build(Map<K,V> map, K key, V value) {
        return build(map, new Object[][]{{key,value}});
    }

    /**
     * Usage:
     *  Map<K,V> myMap = MapBuilder.build(MyMapClass.class, new Object[][]{ {k,v}, {k,v}, ... });
     * @param mapClass a Class that implements Map
     * @return the map passed in, now containing new "values" mappings
     */
    public static <K,V> Map<K,V> build(Class<? extends Map<K,V>> mapClass, Object[][] values) {

        final Map<K,V> map;
        try { map = mapClass.newInstance(); } catch (Exception e) {
            throw new IllegalStateException("Couldn't create new instance of class: "+mapClass.getName(), e);
        }
        return build(map, values);
    }

    /** Usage: Map<K,V> myMap = MapBuilder.build(key, value); */
    public static <K,V> Map build(K key, V value) {
        Map<K,V> map = new HashMap<>();
        map.put(key, value);
        return map;
    }

}


Since Java 9, you can create unmodifiable maps out of the box -

Map<String, String> emptymap = Map.of();
Map<String, String> map = Map.of("key1", "val1"); 

there are variants supporting upto 10 keypairs. https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Map.html#of()

This is also supported https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Map.html#ofEntries(java.util.Map.Entry...)

 import static java.util.Map.entry;

 Map<Integer,String> map = Map.ofEntries(
     entry(1, "a"),
     entry(2, "b"),
     entry(3, "c"),
     ...
     entry(26, "z"));
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜