Вы можете использовать flatMap
с закрывающим действием:
Stream<String> generateStream() {
return Stream.of("hello", "world")
.flatMap(s -> Stream.of(s).onClose(() -> System.out.println(s+" consumed")));
}
, которое работает по назначению:
generateStream().forEach(System.out::println);
hello
hello consumed
world
world consumed
даже для операций короткого замыкания:
Optional<String> o = generateStream().filter(s -> s.startsWith("he")).findFirst();
o.ifPresent(s -> System.out.println("found "+s));
hello consumed
found hello
Но обратите внимание, что это может иметь c драматическое * влияние на производительность по сравнению с операцией без flatMap
. Но для целей отладки или в случаях, когда производительность не имеет значения, это может быть полезно.
Кроме того, имейте в виду, что потребление конвейером Stream не означает, что операция терминала не будет ссылка на него и не прекращать доступ к нему.
Это работает таким образом для операции forEach
, но, например, для reduce((a,b) -> a)
, все элементы потребляются один за другим, но ссылка на первый элемент будут храниться до конца и даже возвращаться в качестве окончательного результата. Для min(comparator)
любой элемент может удерживаться до тех пор, пока не будет обнаружен меньший. И, наконец, такие операции, как toArray()
, удерживают и возвращают все элементы в результате.
Кроме того, операции с отслеживанием состояния могут отделять последующую конвейерную обработку от исходного потока. Например, шаг sorted
может действовать как toArray
, буферизуя все элементы, заставляя их отображаться как потребленные для источника, перед сортировкой массива и продолжением потоковой передачи по массиву. Точно так же distinct
будет содержать ссылки на объекты за пределами их потребления и в параллельном потоке, их последующая обработка может быть отложена до точки после закрытия исходного потока.