One of the missing features in the Streams API is the "partition by" transformation, for example as defined in Clojure. Say I want to reproduce Hibernate's fetch join: I want to issue a single SQL SELECT statement to receive this kind of objects from the result:
class Family {
String surname;
List<String> members;
}
I issue:
SELECT f.name, m.name
FROM Family f JOIN Member m on m.family_id = f.id
ORDER BY f.name
and I retrieve a flat stream of (f.name, m.name)
records. Now I need to transform it into a stream of Family
objects, with a list of its members inside. Assume I already have a Stream<ResultRow>
; now I need to transform it into a Stream<List<ResultRow>>
and then act upon that with a mapping transformation which turns it into a Stream<Family>
.
The semantics of the transformation are as follows: keep collecting the stream into a List
for as long as the provided discriminator function keeps returning the same value; as soon as the value changes, emit the List
as an element of the output stream and start collecting a new List
.
I hope to be able to write this kind of code (I already have the resultStream
method):
Stream<ResultRow> dbStream = resultStream(queryBuilder.createQuery(
"SELECT f.name, m.name"
+ " FROM Family f JOIN Member m on m.family_id = f.id"
+ " ORDER BY f.name"));
Stream<List<ResultRow> partitioned = partitionBy(r -> r.string(0), dbStream);
Stream<Family> = partitioned.map(rs -> {
Family f = new Family(rs.get(0).string(0));
f.members = rs.stream().map(r -> r.string(1)).collect(toList());
return f;
});
Needless to say, I expect the resulting stream to stay lazy (non-materialized) as I want to be able to process a result set of any size without hitting any O(n) memory limits. Without this crucial requirement I would be happy with the provided groupingBy
collector.
groupingBy
and the key distinction. The solutions provided are quite different, though. – Compensation