Non terminal operation peek() in Java8
In Java 8, the method is a non-terminal operation that allows you to perform an action on each element of a stream as it passes through the pipeline. It can be useful for debugging or logging, or for performing some kind of side effect on the stream elements.
The method takes a
Consumer
as its argument, which is a functional interface that takes a single argument and returns no result. The Consumer
can be a lambda expression, method reference, or an instance of a class that implements the Consumer
interface.
An example of using to debug a stream of integers:
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
IntStream.range(1, 6)
.peek(i -> System.out.println("Before map: " + i))
.map(i -> i * i)
.peek(i -> System.out.println("After map: " + i))
.sum();
}
}
In this example, we’re creating a stream of integers from 1 to 5 using the IntStream.range
method. We then call the peek
method twice to print out each integer before and after the map
operation. The map
operation squares each integer in the stream, and the sum
operation calculates the sum of the squared integers. However, since we're not doing anything with the result, the code doesn't actually return anything.
When you run this code, you’ll see the following output:
Before map: 1
After map: 1
Before map: 2
After map: 4
Before map: 3
After map: 9
Before map: 4
After map: 16
Before map: 5
After map: 25
As you can see, the peek
method allows us to see the values of each element in the stream before and after the map
operation. This can be very helpful for debugging or understanding how a stream is being transformed.
Example of using peek
to log the processing of a stream of strings:
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
Stream.of("apple", "banana", "cherry", "date", "elderberry")
.peek(s -> System.out.println("Before filter: " + s))
.filter(s -> s.length() > 5)
.peek(s -> System.out.println("After filter: " + s))
.forEach(System.out::println);
}
}
In this example, we’re creating a stream of strings and using peek
to print out each string before and after the filter
operation. The filter
operation filters out any strings with a length less than or equal to 5, and the remaining strings are printed out using forEach
. When you run this code, you'll see the following output:
Before filter: apple
After filter: banana
banana
Before filter: cherry
After filter: elderberry
elderberry
As you can see, the peek
method allows us to see which strings are being filtered out and which ones are passing through the filter. This can be very helpful for understanding how a stream is being processed and for debugging any issues that arise.
In addition to debugging or logging, there are other use cases for the peek
method in Java 8.
One such use case is for updating shared state while processing a stream. Since stream operations are generally stateless, it can be difficult to update shared state between operations. However, the peek
method can be used to perform side effects on the stream elements and update shared state as needed.
Example of using peek
to count the number of times a certain value appears in a stream:
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
int[] count = {0};
Stream.of("apple", "banana", "cherry", "date", "elderberry")
.peek(s -> {
if (s.startsWith("b")) {
count[0]++;
}
})
.forEach(System.out::println);
System.out.println("Count: " + count[0]);
}
}
In this example, we’re creating a stream of strings and using peek
to check if each string starts with the letter "b". If it does, we increment the count
array, which is a shared state variable. After processing the stream, we print out the value of the count
variable. When you run this code, you'll see the following output:
apple
banana
cherry
date
elderberry
Count: 1
As you can see, the peek
method allows us to update the count
variable while processing the stream.
Another use case for the peek
method is for generating side effects that are necessary for the operation of the stream pipeline. For example, you might use peek
to perform a validation check on each element in a stream before processing it further. If an element fails the validation check, you might want to stop processing the stream and return an error message.
An example of using peek
to validate a stream of integers:
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
IntStream.range(1, 6)
.peek(i -> {
if (i == 3) {
throw new RuntimeException("Invalid value: " + i);
}
})
.map(i -> i * i)
.sum();
}
}
In this example, we’re creating a stream of integers from 1 to 5 and using peek
to check if each integer is equal to 3. If an integer is equal to 3, we throw a RuntimeException
with an error message. Since the peek
method is a non-terminal operation, it allows us to stop processing the stream if an error occurs. When you run this code, you'll see the following output:
Exception in thread "main" java.lang.RuntimeException: Invalid value: 3
at Main.lambda$0(Main.java:8)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.stream.IntPipeline$4$1.accept(IntPipeline.java:250)
at java.base/java.util.stream.Streams$StreamBuilderImpl$StreamImpl$WithBuilder.accept(Streams.java:762)
at java.base/java.util.stream.Streams$ConcatSpliterator.tryAdvance(Streams.java:734)
at java.base/java.util.stream.Streams$ConcatSpliterator.tryAdvance(Streams.java:2117)
at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:129)
at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527)
at java.base/java.util.stream.AbstractPipeline
Thanks, before you go:
- 👏 Please clap for the story and follow the author 👉
- Please share your questions or insights in the comments section below. Let’s help each other and become better Java developers.
- Let’s connect on