Jin Kwon
Jin Kwon

Reputation: 21978

How can I map stream with grouping?

I have a very long stream of children.

// ordered by parent.id / child.id
Stream<Child> childStream;

say,

Child(id = 1, parent(id = 1))
Child(id = 2, parent(id = 1))
Child(id = 3, parent(id = 2))
Child(id = 4, parent(id = 2))
Child(id = 5, parent(id = 3))

Each Child has a parent.

class Child {
    Parent parent;
}

Now, how can I map the stream into a stream of Family?

class Family {
    Parent parent;
    List<Child> children;
}

I already know Collectors.groupingBy, but the stream is so long that collecting them all into a Map is not applicable.

Upvotes: 3

Views: 99

Answers (2)

123-xyz
123-xyz

Reputation: 637

Here is the solution by StreamEx if the stream is ordered by parent(id).

StreamEx.of(childStream)
        .collapse((a, b) -> a.getParent().getId() == b.getParent().getId(), Collectors.toList())
        .map(cl-> new Family(cl.get(0).getParent(), cl))...;

collapse is lazy evaluation comparing groupBy. For Example, if you only want to get first 5 families, only the children in first 5 families will be loaded, not all.

StreamEx.of(childStream)
        .collapse((a, b) -> a.getParent().getId() == b.getParent().getId(), Collectors.toList())
        .map(cl-> new Family(cl.get(0).getParent(), cl))
        .limit(5);

Upvotes: 1

Eran
Eran

Reputation: 393771

In order to group your Child instances into Family instances, you must process the Stream, so a terminal operation is required. You can use groupingBy and then transform the resulting Map into the Stream you need:

Stream<Family> families = 
    childStream.collect(Collectors.groupingBy(Child::getParent))
               .entrySet()
               .stream()
               .map(entry -> new Family(entry.getKey(),entry.getValue()));

This is assuming your Family class has a Family(Parent parent, List<Child> children) constructor.

Upvotes: 6

Related Questions