Radosław Łazarz Radosław Łazarz - 3 months ago 14
Java Question

Is it possible to define an optional flow or exception-like behaviour in Java 8 streams API?

Let us have a stream of objects, resulting from a sequence of operations (e.g. mapping, filtering, flatmapping, etc.). Now I want to do a certain operation on them, but only if a given predicate is true. Otherwise I want to immediately return something else.

A simple example. I have a stream of different food objects. If all of them are edible I want to perform a cook operation on them and return the list of cooked food. But if any of them turns out to not be edible I want to immediately return an empty list.

Few solutions come to my mind, but I am not satisfied with any of them.

I could first perform an

allMatch
operation with
isEdible
predicate on the stream, but it will result in terminating it and I would need to repeat preliminary operations once more.

I could persist the collection that is the result of preliminary operations before checking the edibility, but therefore I need to perform them for all elements. Which is suboptimal, because it may turn out, that the first of them is not edible and
allMatch
would return much, much earlier.

Or I could design a hacky
reduce
routine, but it would also be unable to stop processing elements when predicate fails.

What I hope for is something like the code below. Is it possible with current API?

source.stream()
// some operations
.ifAny(food -> !food.isEdible(), new LinkedList<Food>())
// other operations if previous step not failed
.peek(food -> food.prepare())
.collect(Collectors.toList());

Answer

Not exactly what you wanted, but using the ternary operator will make your two step solution look cleaner, it also should have optimal performance:

return source.stream()
    .allMatch(this::isEdible)
    ? source.stream()
        .map(food -> {
           food.prepare(); // do stuff
           return food;
        })
        .collect(Collectors.toList())
    : Collections.emptyList();