How do you group elements in a List<P> to a Map<K, List<V>> while retaining order?

You're really close to what you want:

Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
            places.stream()
                  .collect(Collectors.groupingBy(
                          PlaceSummary::getPlaceId,
                          LinkedHashMap::new,
                          Collectors.mapping(Function.identity(), Collectors.toList())
                  ));

In the Collectors.mapping method, you need to give the PlaceSummary instance and not the place ID. In the code above, I used Function.identity(): this collector is used to build the values so we need to accumulate the places themselves (and not their ID).

Note that it is possible to write directly Collectors.toList() instead of Collectors.mapping(Function.identity(), Collectors.toList()).

The code you have so far does not compile because it is in fact creating a Map<String, List<String>>: you are accumulating the IDs for each ID (which is quite weird).


You could write this as a generic method:

private static <K, V> Map<K, List<V>> groupByOrdered(List<V> list, Function<V, K> keyFunction) {
    return list.stream()
                .collect(Collectors.groupingBy(
                    keyFunction,
                    LinkedHashMap::new,
                    Collectors.toList()
                ));
}

and use it like this:

Map<String, List<PlaceSummary>> placesGroupedById = groupByOrdered(places, PlaceSummary::getPlaceId);

I think you got a little confused about the final collector. It merely represents what needs to be in each map value. There is no need to have a secondary mapping collector, as you just want a list of the original objects.

    Map<String, List<PlaceSummary>> placesGroupedByPlaceId =
          places.stream()
                .collect(Collectors.groupingBy(PlaceSummary::getPlaceId,
                                               LinkedHashMap::new,
                                               Collectors.toList()));