peek

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 5 min

TL;DR

  • peek(Consumer<T>) is an intermediate operation primarily used for debugging.
  • It allows you to perform a non-interfering action on each element as it flows through the pipeline.
  • It does not consume the stream (unlike forEach) and returns the stream unmodified.

Concept

Because Stream operations are lazy and chained together, debugging them can be a nightmare. If the output of your pipeline is wrong, you can’t easily put a breakpoint inside a .map() or .filter() to see what the data looks like at that specific step.

peek() solves this. It takes a Consumer (which returns void). It temporarily pauses the flow of that element, executes the consumer (usually System.out.println), and then sends the exact same element to the next stage in the pipeline.

Examples

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

public class PeekExample {
    public static void main(String[] args) {
        
        List<String> words = Arrays.asList("apple", "banana", "cherry", "date");
        
        // Using peek to debug the pipeline step-by-step
        List<String> result = words.stream()
            .peek(w -> System.out.println("1. Fetched from source: " + w))
            
            .filter(w -> w.length() > 4)
            .peek(w -> System.out.println("2. Passed filter: " + w))
            
            .map(String::toUpperCase)
            .peek(w -> System.out.println("3. Mapped to uppercase: " + w))
            
            .collect(Collectors.toList());
            
        // The output will show the exact flow of elements.
        // E.g.
        // 1. Fetched from source: apple
        // 2. Passed filter: apple
        // 3. Mapped to uppercase: APPLE
        // 1. Fetched from source: banana
        // ...
    }
}

Interview Questions

Q: Can you use peek() to modify the elements in the stream?
A: You shouldn’t. The official Java documentation states that the action passed to peek should be non-interfering. While you could technically modify the internal state of a mutable object inside peek(), doing so violates functional programming principles and makes the code confusing. If you want to modify elements, you should use map().

Q: Why does my peek() not print anything?
A: If your peek() doesn’t print anything, you likely forgot to attach a Terminal Operation to the stream (like collect() or count()). Because peek() is an intermediate operation, it is lazy. The pipeline will not execute, and data will not flow through peek(), until a terminal operation is invoked.