views:

134

answers:

7

In short, if you want to write a map of e.g. constants in Java, which in e.g. Python and Javascript you 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?

+5  A: 

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.
  );
Jorn
I did not say there was. I will look into it. Thanks.
FK82
+2  A: 

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
    }
}
Tony Ennis
This doesn't make sure your map can't change at all. You would be better off creating the map in your static block, then assigning the field using Collections.unmodifiableMap()
Jorn
Correct. I wasn't addressing mutability but using standard Java to make a good stab at a 'constant' map. I'll check out `unmodifiableMap()` - good tip.
Tony Ennis
`public Map<String,String> getMap() { return Collections.unmodifiableMap(map); }` seems to allow read access to the map from outside our class.
Tony Ennis
_maps that won't be changing_ was a poor choice of words. I mean, _maps you have no intention of changing_.
Tony Ennis
+1  A: 

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.

Crozin
+5  A: 

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.

BalusC
I was thinking about an `enum` myself. However the official documentation on that type is not very informative. Thanks for the example. Your comment on my variable naming -- I might add -- is essentially superfluous however.
FK82
You're welcome. The comment was targeted on the enum name `Constants` in the example, not on your code snippet :)
BalusC
Ok well, my bad then. Thanks again.
FK82
On a note: is extending the enum type with a `value` method necessary? The `Enum` `class` -- which all `enum` types implement -- already has a method `valueOf`.
FK82
No, the `value()` just returns whatever you've passed into the enum's constructor, in this example `Value1`, `Value2` and `Value3`. The `valueOf()` method is to convert string literals `"NAME_1"`, `"NAME_2"` and `"NAME_3"` to real enum types. Useful when for example mapping a varchar in DB into a Java enum type.
BalusC
Ok, true. I wasn't reading closely. Anyhow, what do I need this method for? the Name and Value constants are already static members of the `enum` type. You don't need to pass them as arguments trough the constructor, do you?
FK82
I mentioned an example: *Useful when for example mapping a varchar in DB into a Java enum type.* Enums are not constructable by yourself. They are already constructed during classloading. There is always ever only one instance of each. The `valueOf()` just returns the `enum` instance as represented by the given `String` name. So, it's useful whenever you have the enum name in flavor of a `String` for some reason (DB column, text file, user input, etc) and want to get a *real* enum instance out of it. E.g. `Constants name1 = Constants.valueOf("NAME_1");` returns `Constants.NAME1`.
BalusC
I see. Thanks for your time!
FK82
I understand how this works now. The `enum` literal notation basically acts as a static block would and initializes `Enum` `Object` s according to the declaration block on top with the fields and methods below. This is kind of convoluted, but it does the trick.
FK82
Yes. Lookup the ["type safe" pattern](http://java.sun.com/developer/Books/shiftintojava/page1.html), which was used there back in the pre-Java 1.5 ages when Java didn't have the `enum`. It describes roughly how it works under the covers.
BalusC
Alright. Thanks again.
FK82
+1  A: 

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");
    }
}
Tony Ennis
Thanks for your answer. I probably have to clarify that, the focus of my question is on the literal writing pattern (known from among others Python and JavaScript), not on immutability. If it was, a `Map` implementation with `static final` fields would probably suffice.
FK82
@FK82: `static final` does not make an immutable map. An immutable map is one where no keys may be added, and no values of existing keys may be changed -- this is not true for a map declared as `static final`.
Mark E
Point taken. I was however talking about the immutability of the fields in a `class` implementing `Map`.
FK82
+1  A: 

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;
    }
}
Tony Ennis
To keep it clean, you could have edited your original post. :-)
FK82
+1  A: 

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
);
Mark E
I was thinkin about something like that. Might be the next best solution to using an `enum` type in terms of ease of writing.
FK82