5

What is the easiest way to convert a 2D array of Strings into a HashMap?

For example, take this:

final String[][] sheetMap = { /* XSD Name,  XSL Sheet Name */
                              {"FileHeader", "FileHeader"}, 
                              {"AccountRecord", "AccountRecord"}, 
                              {"DriverCardRecord", "DriverCardRecord"}, 
                              {"AssetCardRecord", "AssetCardRecord"},
                              {"SiteCardRecord", "SiteCardRecord"}
                            };

This is most likely going to be loaded from a file and will be much bigger.

tangens
  • 39,095
  • 19
  • 120
  • 139
Casey
  • 12,070
  • 18
  • 71
  • 107
  • I should probably add that other than just looping through the elements, which I am doing now, is there a Collections method I can use just to pass this 2D array into a HashMap instance? – Casey Nov 09 '09 at 17:41
  • 1
    Not that I'm aware of - and I don't think it's *possible* to implement something more efficient than a simple loop over all the array elements. So you wouldn't gain anything above some brevity, which you could again duplicate by putting this in your own `Utils`-type class. – Andrzej Doyle Nov 09 '09 at 17:44

7 Answers7

23
final Map<String, String> map = new HashMap<String, String>(sheetMap.length);
for (String[] mapping : sheetMap)
{
    map.put(mapping[0], mapping[1]);
}
Andrzej Doyle
  • 102,507
  • 33
  • 189
  • 228
6

If you just want to initialize your map in a convenient way, you can use double brace initialization:

Map<String, String > sheetMap = new HashMap<String, String >() {{
   put( "FileHeader", "FileHeader" ); 
   put( "AccountRecord", "AccountRecord" ); 
   put( "DriverCardRecord", "DriverCardRecord" ); 
   put( "AssetCardRecord", "AssetCardRecord" );
   put( "SiteCardRecord", "SiteCardRecord" );
}};
Community
  • 1
  • 1
tangens
  • 39,095
  • 19
  • 120
  • 139
  • This can have some negative consequences and is widely considered an anti-pattern -- https://blog.jooq.org/2014/12/08/dont-be-clever-the-double-curly-braces-anti-pattern/ – Jamie Bisotti Feb 17 '17 at 20:14
  • Not to mention, it doesn't actually answer the question. It doesn't convert from or even involve a 2D array. – Tim M. Jan 25 '19 at 03:26
4

As a slightly cleaner alternative to tradeJmark answer:

String[][] arr = // your two dimensional array
Map<String, String> arrMap = Arrays.stream(arr).collect(Collectors.toMap(e -> e[0], e -> e[1]));

// Sanity check!
for (Entry<String, String> e : arrMap.entrySet()) {
    System.out.println(e.getKey() + " : " + e.getValue());
}
dwellman
  • 175
  • 2
  • 8
  • Wow, that's a lot nicer than mine, I can't believe I didn't know this existed! That's great, thanks! – Tim M. Jan 25 '19 at 03:23
2

Wait; if this is going to be loaded from a file, don't go through the intermediate array step! You would have had to load it all first before creating the array or you wouldn't know the size for the array. Just create a HashMap and add each entry as you read it.

Kevin Bourrillion
  • 40,336
  • 12
  • 74
  • 87
  • Yeah, that's the plan. However, I don't know where the information is going to be stored at the moment so I just hard coded it into the array. It did bring up the question of if the Collections framework already contained a utility method to accomplish this task, even though it is trivial to implement. I would rather use a method in the collections wherever possible. – Casey Nov 10 '09 at 13:07
  • ah. well, yeah, it doesn't. if it did, it would accept a Collection of something, not an array. Maybe of Map.Entry's. But this is not really very useful. Looping is better. – Kevin Bourrillion Nov 10 '09 at 20:44
2

The existing answers work well, of course, but in the interest of continually updating this site with new info, here's a way to do it in Java 8:

String[][] arr = {{"key", "val"}, {"key2", "val2"}};
HashMap<String, String> map = Arrays.stream(arr)
        .collect(HashMap<String, String>::new,
                (mp, ar) -> mp.put(ar[0], ar[1]),
                HashMap<String, String>::putAll);

Java 8 Streams are awesome, and I encourage you to look them up for more detailed info, but here are the basics for this particular operation:

  • Arrays.stream will get a Stream<String[]> to work with.
  • collect takes your Stream and reduces it down to a single object that collects all of the members. It takes three functions. The first function, the supplier, generates a new instance of an object that collects the members, so in our case, just the standard method to create a HashMap. The second function, the accumulator, defines how to include a member of the Stream into the target object, in your case we simply want to put the key and value, defined as the first and second value from each array, into the map. The third function, the combiner, is one that can combine two of the target objects, in case, for whatever reason, the JVM decided to perform the accumulation step with multiple HashMaps (in this case, or whatever other target object in another case) and then needs to combine them into one, which is primarily for asynchronous execution, although that will not typically happen.
Tim M.
  • 598
  • 5
  • 15
2

More concise with streams would be:

 import static java.util.Arrays.stream;
 import static java.util.stream.Collectors.toMap;
 import java.util.Map;

     ...

 public static Map<String, String> asMap(String[][] data) {
     return stream(data).collect(toMap( m->m[0], m->m[1] ));
 }

     ...
Simon Jenkins
  • 688
  • 8
  • 11
0

Java 8 way

  public static Map<String, String> convert2DArrayToMap(String[][] data){
    return Arrays.stream(data).collect(Collectors.toMap(m -> m[0], m -> m[1]));
  }

with loop

public static Map<String, String> convert2DArrayToMap(String[][] data)
  {
    Map<String, String> map = new HashMap<>();
    for (String[] m : data)
    {
      if (map.put(m[0], m[1]) != null)
      {
        throw new IllegalStateException("Duplicate key");
      }
    }
    return map;
  }
Ravinda Lakshan
  • 1,006
  • 14
  • 14