How to collect into a Map forming a List in value when duplicate keys in streams, in Java 8

Viewed 6994

I have a stream of elements in the form of either 2-D array or EntrySet. I need these to be collected in a Map. Now the issue is the stream of elements can have duplicate elements. Let's say I want the value to be a list:

Map<String,List<String>>

Example :

class MapUtils
{
// Function to get Stream of String[]
private static Stream<String[]> getMapStream()
{
    return Stream.of(new String[][] {
            {"CAR", "Audi"},
            {"BIKE", "Harley Davidson"},
            {"BIKE", "Pulsar"}
    });
}

// Program to convert Stream to Map in Java 8
public static void main(String args[])
{
    // get stream of String[]
    Stream<String[]> stream = getMapStream();

    // construct a new map from the stream
    Map<String, String> vehicle =
            stream.collect(Collectors.toMap(e -> e[0], e -> e[1]));

    System.out.println(vehicle);
}
}

Output :

java.lang.IllegalStateException: Duplicate key Harley Davidson

I would like to have a way where

  1. I can operate on e->e[0] and e->e[1] to have the problem solved. Is that possible? For this I need an access of the current map object that's getting collected. I am not sure if that makes sense.
  2. A way where this can be achieved with Java 8 streams.

Expected Output :

{CAR=[Audi], BIKE=[Harley Davidson, Pulsar]}
2 Answers

That's what groupingBy is for:

Map<String,List<String>> vehicle = 
    stream.collect(Collectors.groupingBy(e -> e[0], 
                   Collectors.mapping(e -> e[1],
                                      Collectors.toList())));

Output map:

{CAR=[Audi], BIKE=[Harley Davidson, Pulsar]}

You can use groupingBy

getMapStream()
      .map(item -> Arrays.asList(item))
      .collect(Collectors.groupingBy(l->l.get(0),
           Collectors.mapping(l1->l1.get(1),Collectors.toList())));

or use toMap() with merge function.

     Map<String,List<String>> vehicle = getMapStream()
            .collect(Collectors.toMap(item->item[0],
       item->new ArrayList<>(Arrays.asList(item[1])),
                            (l1,l2)->{l1.addAll(l2);return l1;}));
Related