How to debug Java 8 Stream Pipeline - peek() method Example Tutorial (original) (raw)

The peek() method returns a stream consisting of the elements of this stream and performs the action requested by the client. The peek() method expects a Consumer functional interface to perform a non-interfering action on the elements of this stream, usually printing them using the forEach() method.

Btw, the sole reason for using peek() is debugging the Stream pipeline, even the API itself says that peek() is only there for debugging, but it does help to understand the lazy evaluation technique Stream uses to improve performance.

Lazy evaluation means nothing is evaluated in the Stream until a terminal method like forEach(), collect(), or reduce() is called and processing stops as soon as the result is obtained, which means not all the elements of Stream is processed always.

It all depends upon what kind of result you want from Stream. For example, if you call the findFirst() method then as soon as it finds the first element fulling the criterion, processing stops. If you want to understand lazy evaluation in-depth and other Stream features then I highly recommend you check out these Java collections and Stream courses from Udemy and Pluralsight.

Java 8 Stream peek() method Example

In order to understand the peek() method better, let's see some code in action. How about using the filter and map methods in a chained pipeline?

This is a very common code in Java 8 and will help you to learn how stream pipeline processing works in Java 8? What happens in each step? What is the output or data in the stream after each step etc?

Consider the following example, which calls the peek() method after each step in a Stream pipeline involving filter() and map() methods:

List result = Stream.of("EURO/INR", "USD/AUD", "USD/GBP", "USD/EURO") .filter(e -> e.length() > 7) .peek(e -> System.out.println("Filtered value: " + e)) .map(String::toLowerCase) .peek(e -> System.out.println("Mapped value: " + e)) .collect(Collectors.toList());

In this example, we have a Stream of String and then we are filtering all Strings whose length is greater than 7 and then we are converting them to lowercase using the map() function.

Now, what do you think, how will this program execute? top to bottom or bottom to top?

Many of you will think that after the first filter() execution you will get a Stream containing two elements "EURO/INR" and "USD/EURO" and peek() will print those two elements.

Well, that's not the case, since Streams are executed lazily, nothing will happen until the collect() method will execute, which is the terminal method.

This is proved by the following output from running the above code into Eclipse IDE or command prompt, it will print the following lines:

Filtered value: EURO/INR Mapped value: euro/inr Filtered value: USD/EURO Mapped value: usd/euro

The key point to note here is that values are filtered and mapped one by one, not together. It means the code is executed backward when the collect() method calls the Collectors.toList() to get the result in a List, it asks map() function which in turn asks the filter() method.

Since filter() is lazy it returns the first element whose length is greater than 7 and sits back until map() asks again.

You can see that peek() method clearly prints the value of the stream in the pipeline after each call to filter() method. You can further join From Collections to Streams in Java 8 Using the Lambda Expressions course on Pluralsight to learn more about different types of operation with Streamlike intermediate and terminal operation.

How to debug Java 8 Stream Pipeline - peek() method Example Tutorial

How to use peek() method in Java 8

As I said, the Stream.peek() method is very useful for debugging and understating the stream-related code in Java. Here is a couple of more example of using peek() to understand how bulk data operations are processed by Stream utility.

package test;

import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors;

/**

}

Output After the first filter: Lollipop After the first filter: Jelly Bean After the first filter: Ice Cream Sandwich After the first filter: Honeycomb After the second filter: Honeycomb After the first filter: Gingerbread

By looking at this output, can you explain how the code would have been executed? Well, it seems that when to collect() ask the second filter() method, it further asks the first filter() and you can see that the first element Lollipop passed the first filter but couldn't pass the second one because it doesn't start with letter "H".

So, the second filter() again asks the first() filter for an element, it returns Jelly Bean, Ice Cream Sandwich, and HoneyComb one by one. Since HoneyComb made past the second filter it is collected by Collector and again the same process happens but aborted after GingerBread because all elements in Stream are already processed.

This clearly explains the lazy execution behavior of Stream as opposed to eager iterative implementation and the peek() method definitely helps you to understand this better, but if you want to learn Stream in-depth, I suggest you further check these Java Functional Programming and Stream API courses.

Java 8 Stream peek() example for debugging

Important points

  1. The peek() method of Stream class is an intermediate method, hence you can call other stream methods after this.

  2. It returns a new Stream, which is basically the stream it got.

  3. It accepts an object of functional interface Consumer to perform non-interfering action e.g. printing values.

  4. For parallel stream pipelines, the action may be called at whatever time and whatever thread the element is made available by the upstream operation.

Btw, peek() is not the only way to figure out what goes inside a Stream pipeline, you can also use your IDEs to do the heavy work. For example, If you are using IntelliIDEA from JetBrains, you can also use their Java Stream Debugger Plugin to easily debug Java 8 code using map, filter, and collect in IDE itself, like shown in the following GIF diagram:

How to debug Java 8 code with map and filter

That's all about how to use the peek() method in Java 8. You can use the peek() method for debugging. It allows you to see the elements as they flow past a certain point in the pipeline. By using this you can check whether your filter() method is working properly or not. You can see exactly which elements are got filtered by using peek() in Java 8.