Одной из недостающих функций API Streams является преобразование "разделение", например, как определено в Clojure. Скажем, я хочу воспроизвести соединение Hibernate fetch: я хочу выпустить одну инструкцию SQL SELECT для получения таких объектов из результата:
class Family {
String surname;
List<String> members;
}
Я выпускаю:
SELECT f.name, m.name
FROM Family f JOIN Member m on m.family_id = f.id
ORDER BY f.name
и я получаю плоский поток записей (f.name, m.name). Теперь мне нужно преобразовать его в поток объектов Family, со списком его членов внутри. Предположим, что у меня уже есть Stream<ResultRow>; теперь мне нужно преобразовать его в Stream<List<ResultRow>>, а затем действовать на него с преобразованием отображения, которое превращает его в Stream<Family>.
Семантика преобразования заключается в следующем: продолжайте собирать поток в List до тех пор, пока предоставленная функция дискриминатора продолжает возвращать одно и то же значение; как только значение изменится, испустите List как элемент выходного потока и начните собирать новый List.
Я надеюсь, что смогу написать такой код (у меня уже есть метод resultStream):
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;
});
Разумеется, я ожидаю, что результирующий поток останется ленивым (не материализованным), поскольку я хочу иметь возможность обрабатывать результирующий набор любого размера, не затрагивая никаких ограничений памяти O (n). Без этого важного требования я был бы доволен предоставленным коллекционером groupingBy.