Sometimes when processing a Java stream() I find myself in need of a non-terminal forEach() to be used to trigger a side effect but without terminating processing.
I suspect I could do this with something like .map(item -> f(item)) where the method f performs the side effect and returns the item to the stream, but it seems a tad hokey.
Is there a standard way of handling this?
Stream forEach() method in Java with examplesStream forEach(Consumer action) performs an action for each element of the stream. Stream forEach(Consumer action) is a terminal operation i.e, it may traverse the stream to produce a result or a side-effect.
forEach() on List. The forEach() method is a terminal operation, which means that after we call this method, the stream along with all of its integrated transformations will be materialized.
Avoiding the Use of the Reduce MethodA stream does not process any data if it does not end with a terminal operation. We already covered the terminal operation reduce() , and you saw several terminal operations in other examples. Let us now present the other terminal operations you can use on a stream.
Non-Terminal operation : will transform a stream into another stream, such as filter(Predicate).
Yes there is. It is called peek()
(example from the JavaDoc):
Stream.of("one", "two", "three", "four") .peek(e -> System.out.println("Original value: " + e)) .filter(e -> e.length() > 3) .peek(e -> System.out.println("Filtered value: " + e)) .map(String::toUpperCase) .peek(e -> System.out.println("Mapped value: " + e)) .collect(Collectors.toList());
No, there is not.
peek()
will only operate on all elements when forced to by a following operation. Can you predict what will be printed by this code?
public class Test { private static final AtomicBoolean FLAG = new AtomicBoolean(false); private static void setFlagIfGreaterThanZero(int val) { if (val > 0) { FLAG.set(true); } } public static void main(String[] args) { // Test 1 FLAG.set(false); IntStream.range(0, 10) .peek(Test::setFlagIfGreaterThanZero) .findFirst(); System.out.println(FLAG.get()); // Test 2 FLAG.set(false); IntStream.range(0, 10) .peek(Test::setFlagIfGreaterThanZero) .sorted() .findFirst(); System.out.println(FLAG.get()); // Test 3 FLAG.set(false); IntStream.range(0, 10) .peek(Test::setFlagIfGreaterThanZero) .filter(x -> x == 0) .toArray(); System.out.println(FLAG.get()); // Test 4 FLAG.set(false); IntStream.range(0, 10) .boxed() .peek(Test::setFlagIfGreaterThanZero) .sorted() .findFirst(); System.out.println(FLAG.get()); } }
The answer is:
false
false
true
true
That output might be intuitive if you have a solid understanding of Java Streams, but hopefully it also indicates that it's a very bad idea to rely on peek()
as a mid-stream forEach()
.
map()
also suffers the same issue. As far as I'm aware, there is no Stream operation that guarantees a sort of "process every element without taking shortcuts" behavior in every case independent of the prior and following operations.
Although this can be a pain, the short-circuiting behavior of Streams is an important feature. You might find this excellent answer to another question on this topic to be useful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With