[Somjit's][1] answer shows correct approach however it uses an outer variable (errors) to accumulate the errors. This is, in general, discouraged since we should avoid the global/external state.
You can use vavr's [partition][2] method to split the stream into two: one with errors and one with validated ints. They are both put into a tuple:
public void composeExceptions() {
final Tuple2<Stream<Either<IllegalArgumentException, Integer>>, Stream<Either<IllegalArgumentException, Integer>>> both = Stream.range(1, 11)
.map(this::validate)
.partition(Either::isLeft);
both._1.map(Either::getLeft).forEach(e -> System.out.println("Got error: " + e.getMessage()));
both._2.map(Either::get).forEach(i -> System.out.println("Validated correctly: " + i));
}
EDIT
Actually there also other options like:
Stream
.range(1, 11)
.map(this::validate)
.toJavaStream()
.collect(Collectors.teeing(
Collectors.filtering(Either::isLeft, toList()),
Collectors.filtering(Either::isRight, toList()),
(errors, ints) -> new Tuple2<>(errors.stream().map(Either::getLeft), ints.stream().map(Either::get))));
which uses teeing which is quite interesting collector from java API. Unfortunately it mixes vavr and java API which is not great, not terrible.
And:
Stream
.range(1, 11)
.map(this::validate)
.collect(
() -> new Tuple2<>(List.<RuntimeException>empty().asJavaMutable(), List.<Integer>empty().asJavaMutable()),
(tuple, either) -> {
either.peekLeft(tuple._1::add);
either.peek(tuple._2::add);
},
(t1, t2) -> {
t1._1.addAll(t2._1);
t1._2.addAll(t2._2);
}
)
.map((exceptions, integers) -> new Tuple2<>(List.ofAll(exceptions), List.ofAll(integers)));```
which uses vavr API only but underneath uses java `List` since a mutable structure is required here.
[1]: https://stackoverflow.com/a/67556075/542270
[2]: https://www.javadoc.io/doc/io.vavr/vavr/latest/io/vavr/collection/Traversable.html#partition(java.util.function.Predicate)