15

Imagine the following scenario. You have a set of values which are already accessible and known. You have to put them into a HashMap for reasons.

Code example:

String a = "a";
Strinb b = "b";
...
HashMap map = new HashMap(5);
map.put("a", a);
map.put("b", b);
...

Is it really necessary to do it like this? I can't believe that there is not a constructor which let you put in your values from scratch.

I'm talking about something like this:

HashMap map = new HashMap(<"a", a>, <"b", b>, ...);

Or even something like this:

HashMap map = new HashMap(5);
HashMap.putBunchOfStuff("a", a, "b", b, ...);

Edit: My intention was to ask if there is a method but more importantly, if the answer is "no", why there isn't such a constructor/method.

OddDev
  • 3,644
  • 5
  • 30
  • 53
  • Idk if Java8 has some new trickery or not. But [this](http://stackoverflow.com/questions/507602/how-can-i-initialize-a-static-map) question was asked a few years ago and the consensus was no. Also see [this](http://stackoverflow.com/questions/6802483/how-to-directly-initialize-a-hashmap-in-a-literal-way) – chancea May 06 '15 at 14:05
  • you cannot do like this... But you can add another Map into this by using putAll(Map(K,v)); – Gnanadurai A May 06 '15 at 14:06
  • I wonder if it is accessible from the beginning, why don't you use `enums`, or if you don't like duplication for key and value, then why isn't it `Set`, or you can create 'MyHashMap' where you define `put(Object o)` – CsBalazsHungary May 06 '15 at 14:07
  • @CsBalazsHungary It's a little more complicated in the actual problem. This was just pseudo code. – OddDev May 06 '15 at 14:12
  • @OddDev You should always provide code as close to the actual code as possible. Otherwise you might be solving the wrong problem. – Kayaman May 06 '15 at 14:25
  • @Kayaman you should calm down. ;) This code is simplified yes, but there are other smart people besides you who are able to simplify a code without loosing the adaption. – OddDev May 06 '15 at 14:29
  • @OddDev I am perfectly calm, but the description you gave below ("I have to get stuff out of a resultset into hasmaps stored in an array") doesn't reflect the extremely simple example you gave above. – Kayaman May 06 '15 at 14:33

4 Answers4

18

Unfortunately, collection literals were a proposal for Project Coin in Java 7 (and Java 8), but it never made it into the final product, aka it is not a feature of Java.

The proposition was this

Here’s how it would look with map literals:


    final Map<Integer, String> platonicSolids = { 
          4 : "tetrahedron",
          6 : "cube", 
          8 : "octahedron", 
          12 : "dodecahedron", 
          20 : "icosahedron"
    };


Here is the empty map literal, which has a slightly irregular syntax to make
it differ from the empty set:


    Map<String, Integer> noJokeHere = { : };

BUT it never happened, so unfortunately that doesn't work. So unless you write your own magic builder or fancy lambda like on this site of Per-Åke Minborg, you're on your own. The following from the site should work, though (in Java 8).

//copy paste from linked site
Map<Integer, String> map = Stream.of(
            new SimpleEntry<>(0, "zero"),
            new SimpleEntry<>(1, "one"),
            //...
            new SimpleEntry<>(11, "eleven"),
            new SimpleEntry<>(12, "twelve"))
            .collect(Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue()));

And the simplified version, also from the site:

//copy paste from linked site
public static <K, V> Map.Entry<K, V> entry(K key, V value) {
    return new AbstractMap.SimpleEntry<>(key, value);
}

public static <K, U> Collector<Map.Entry<K, U>, ?, Map<K, U>> entriesToMap() {
    return Collectors.toMap((e) -> e.getKey(), (e) -> e.getValue());
}

Map<Integer, String> map = Stream.of(
            entry(0, "zero"),
            //...
            entry(12, "twelve"))
            .collect(entriesToMap());

Collection literals weren't introduced because of these points:

  • The "simple" version of this feature (sets, lists, maps only) is not very satisfying or popular; the "extensible" version of this feature is open-ended, messy, and virtually guaranteed to way overrun its design budget;

  • The library-based version gives us X% of the benefit for 1% of the cost, where X >> 1;

  • Value types are coming, and the "what would this feature look like" in a world with value types may well be quite different than in a world without, suggesting it would be questionable to try and do this work before value types;

  • We are better off focusing our language-design bandwidth on addressing more the foundational issues underlying a library-based version (including: more efficient varargs, array constants in the constant pool, immutable arrays, and support for caching (and reclaiming under pressure) intermediate immutable results).

By Brian Goetz from Oracle

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
1

There's no such constructor by default - you could use the double-brace idiom to put all values in the map on instantiation, but this still requires multiple put calls:

Map<String,String> map = new HashMap<>() {{
    put("a", "val1");
    put("b", "val2");
    //etc.
}};

In reality you usually have large blocks of data you want to put into a map accessible from a stream or another collection, so it's trivial to loop over multiple put calls in this instance.

Guava has a putAll() on its ImmutableMap.Builder, enabling you to do something similar to this before instantiation. Other external libraries may have other similar behaviour, but fact is you'll have to resort to using external libraries.

Michael Berry
  • 70,193
  • 21
  • 157
  • 216
  • 4
    Note: double-brace initialization creates an anonymous subclass which among other things keeps a strong reference to the outer instance (thus keeping it alive) if it's used in a non-static context. – Radiodef May 06 '15 at 14:12
  • 1
    @Radiodef This is true - I don't find it an ideal idiom at all (it's more an accident of obscure syntax than something designed for this use.) – Michael Berry May 06 '15 at 14:15
1

It is possible to create a pretty utility method to create maps using varargs.

Example:

@SuppressWarnings("unchecked")
public static <K, V> HashMap<K, V> newMap(Object... elems) {
    HashMap<K, V> m = new HashMap<K, V>();

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

    return m;
}

Map<String, Integer> m = newMap("k1", 1, "k2", 2);        

Beware: This is not type safe and might result in a map with objects of the wrong types if you make a mistake when using it, throwing ClassCastException in unexpected places. This is called heap pollution. Example:

Map<String, Integer> m = newMap("k1", 1, "k2", "2");
int s = m.get("k2");  // ClassCastException!
Lii
  • 11,553
  • 8
  • 64
  • 88
0

You can use something like that :

HashMap<String, String> map = new HashMap<String, String>(5){{
    put("a", "a");
    put("b", "b");
}};

But I don't know if u can use with local variable a and b.

Gilles
  • 202
  • 1
  • 11
  • 2
    Note: this creates an anonymous subclass which among other things keeps a strong reference to the outer instance (thus keeping it alive) if it's used in a non-static context. – Radiodef May 06 '15 at 14:11