Using the EntryStream class of my StreamEx library such tasks can be solved quite easily:
Map<Integer, List<String>> z = EntryStream.of(x)
.mapKeys(k -> k.p)
.flatMapValues(List::stream)
.grouping();
Internally it's transformed to something like this:
Map<Integer, List<String>> z = x.entrySet().stream()
.map(e -> new AbstractMap.SimpleImmutableEntry<>(e.getKey().p, e.getValue()))
.<Entry<Integer, String>>flatMap(e -> e.getValue().stream()
.map(s -> new AbstractMap.SimpleImmutableEntry<>(e.getKey(), s)))
.collect(Collectors.groupingBy(e -> e.getKey(),
Collectors.mapping(e -> e.getValue(), Collectors.toList())));
So it's actually a single stream pipeline.
If you don't want to use the third-party code, you can simplify the above version a little:
Map<Integer, List<String>> z = x.entrySet().stream()
.<Entry<Integer, String>>flatMap(e -> e.getValue().stream()
.map(s -> new AbstractMap.SimpleEntry<>(e.getKey().p, s)))
.collect(Collectors.groupingBy(e -> e.getKey(),
Collectors.mapping(e -> e.getValue(), Collectors.toList())));
Though it still looks ugly.
Finally please note that in JDK9 there's new standard collector called flatMapping which can be implemented in the following way:
public static <T, U, A, R>
Collector<T, ?, R> flatMapping(Function<? super T, ? extends Stream<? extends U>> mapper,
Collector<? super U, A, R> downstream) {
BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();
return Collector.of(downstream.supplier(),
(r, t) -> {
try (Stream<? extends U> result = mapper.apply(t)) {
if (result != null)
result.sequential().forEach(u -> downstreamAccumulator.accept(r, u));
}
},
downstream.combiner(), downstream.finisher(),
downstream.characteristics().toArray(new Collector.Characteristics[0]));
}
Using this collector, your task can be solved simpler without additional libraries:
Map<Integer, List<String>> z = x.entrySet().stream()
.map(e -> new AbstractMap.SimpleImmutableEntry<>(e.getKey().p, e.getValue()))
.collect(Collectors.groupingBy(e -> e.getKey(),
flatMapping(e -> e.getValue().stream(), Collectors.toList())));