6

I have this class.

class Assignment {
  private Integer index;
  private List<Quantity> quantities;
}

Then, I have a list of objects from that class.

List<Assigment> assignments = new ArrayList<>();

Is there a way to create a Map that contains the index from Assignment and the List<Quantity> as values?

This is what I have tried so far.

assignments.stream().collect(groupingBy(Assignment::getIndex));

But this gives me a Map<Integer, List<Assignment>> and I want a Map<Integer, List<Quantity>>.

I have tried using forEach method - and it workes - but I'm sure there must be a way to do it in one liner - or at least using only collect and groupingBy methods

Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
alayor
  • 4,537
  • 6
  • 27
  • 47
  • 2
    The established alternative is to use a `flatMap` to a pair type before `collect`. See also [this answer](http://stackoverflow.com/a/39131049/2711488). The 3rd alternative would be `.collect(groupingBy(Assignment::getIndex, Collector.of(ArrayList::new, (l,a)->l.addAll(a.getQuantities()), (a,b)->{a.addAll(b); return a; })));` – Holger Jan 26 '17 at 19:57

1 Answers1

15

It looks like there is no flat-mapping collector that you can use as a down-stream for groupingBy in Java8, but it has been proposed and accepted for Java9: https://bugs.openjdk.java.net/browse/JDK-8071600

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) -> mapper.apply(t).sequential().forEach(u -> downstreamAccumulator.accept(r, u)),
                            downstream.combiner(),
                            downstream.finisher(),
                            downstream.characteristics().stream().toArray(Collector.Characteristics[]::new));
    } 

If you use that one, and also add a quantities method to Assignment that returns a Stream<Quantity>, you can use this code:

Map<Integer, List<Quantity>> result = assignments.stream()
    .collect(groupingBy(Assignment::getIndex, 
        flatMapping(Assignment::quantities, toList())));
Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93