You are correct, "peek" in the English sense of the word means "look, but do not touch."

However the JavaDoc states:

peek

Stream peek(Consumer action)

Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.

Key words: "performing ... action" and "consumed". The JavaDoc is very clear that we should expect peek to have the ability to modify the stream.

However the JavaDoc also states:

API Note:

This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline

This indicates that it is intended more for observing, e.g. logging elements in the stream.

What I take from all of this is that we can perform actions using the elements in the stream, but should avoid mutating elements in the stream. For example, go ahead and call methods on the objects, but try to avoid mutating operations on them.

At the very least, I would add a brief comment to your code along these lines:

// Note: this peek() call will modify the Things in the stream.
streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);

Opinions differ on the usefulness of such comments, but I would use such a comment in this case.

Answer from user22815 on Stack Exchange
Top answer
1 of 3
38

You are correct, "peek" in the English sense of the word means "look, but do not touch."

However the JavaDoc states:

peek

Stream peek(Consumer action)

Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.

Key words: "performing ... action" and "consumed". The JavaDoc is very clear that we should expect peek to have the ability to modify the stream.

However the JavaDoc also states:

API Note:

This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline

This indicates that it is intended more for observing, e.g. logging elements in the stream.

What I take from all of this is that we can perform actions using the elements in the stream, but should avoid mutating elements in the stream. For example, go ahead and call methods on the objects, but try to avoid mutating operations on them.

At the very least, I would add a brief comment to your code along these lines:

// Note: this peek() call will modify the Things in the stream.
streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);

Opinions differ on the usefulness of such comments, but I would use such a comment in this case.

2 of 3
1

It could be easily misinterpreted, so I would avoid using it like this. Probably the best option is to use a lambda function to merge the two actions you need into the forEach call. You may also want to consider returning a new object rather than mutating the existing one - it may be slightly less efficient, but it is likely to result in more readable code, and reduces the potential of accidentally using the modified list for another operation that should have received the original.

🌐
Baeldung
baeldung.com › home › java › java streams › java streams peek() api
Java Streams peek() API | Baeldung
January 8, 2026 - The Java Stream API introduces us to a powerful alternative for processing data. In this short tutorial, we’ll focus on peek(), an often misunderstood method.
🌐
Reddit
reddit.com › r/learnprogramming › java 8 intermediate operation - peek
r/learnprogramming on Reddit: Java 8 Intermediate Operation - peek
July 19, 2022 -

Hi There,

I am trying to learn Java 8 concepts and struck at one point while practicing peek operation. As per my knowledge peek is mainly for debugging purpose, where we want to see the elements as they flow past a certain point in a pipeline.

As per my expectation below code snippet should print 1 4 9 3. But its just printing 3, which is the result of count.

List<Integer> list = new ArrayList<>();

list.add(1);

list.add(2);

list.add(3);

System.out.println(list.stream().map(a -> a*a).peek(System.out::println).count());

When I try to user filter, it prints correct result which is 4 1

System.out.println(list.stream().map(a -> a*a).filter(a -> a%2==0).peek(System.out::println).count());

Is there something that I am missing here.

Thanks advance for your help

Top answer
1 of 11
182

The important thing you have to understand is that streams are driven by the terminal operation. The terminal operation determines whether all elements have to be processed or any at all. So collect is an operation that processes each item, whereas findAny may stop processing items once it encountered a matching element.

And count() may not process any elements at all when it can determine the size of the stream without processing the items. Since this is an optimization not made in Java 8, but which will be in Java 9, there might be surprises when you switch to Java 9 and have code relying on count() processing all items. This is also connected to other implementation-dependent details, e.g. even in Java 9, the reference implementation will not be able to predict the size of an infinite stream source combined with limit while there is no fundamental limitation preventing such prediction.

Since peek allows “performing the provided action on each element as elements are consumed from the resulting stream”, it does not mandate processing of elements but will perform the action depending on what the terminal operation needs. This implies that you have to use it with great care if you need a particular processing, e.g. want to apply an action on all elements. It works if the terminal operation is guaranteed to process all items, but even then, you must be sure that not the next developer changes the terminal operation (or you forget that subtle aspect).

Further, while streams guarantee to maintain the encounter order for a certain combination of operations even for parallel streams, these guarantees do not apply to peek. When collecting into a list, the resulting list will have the right order for ordered parallel streams, but the peek action may get invoked in an arbitrary order and concurrently.

So the most useful thing you can do with peek is to find out whether a stream element has been processed which is exactly what the API documentation says:

This method exists mainly to support debugging, where you want to see the elements as they flow past a certain point in a pipeline

2 of 11
123

The key takeaway from this:

Don't use the API in an unintended way, even if it accomplishes your immediate goal. That approach may break in the future, and it is also unclear to future maintainers.


There is no harm in breaking this out to multiple operations, as they are distinct operations. There is harm in using the API in an unclear and unintended way, which may have ramifications if this particular behavior is modified in future versions of Java.

Using forEach on this operation would make it clear to the maintainer that there is an intended side effect on each element of accounts, and that you are performing some operation that can mutate it.

It's also more conventional in the sense that peek is an intermediate operation which doesn't operate on the entire collection until the terminal operation runs, but forEach is indeed a terminal operation. This way, you can make strong arguments around the behavior and the flow of your code as opposed to asking questions about if peek would behave the same as forEach does in this context.

accounts.forEach(a -> a.login());
List<Account> loggedInAccounts = accounts.stream()
                                         .filter(Account::loggedIn)
                                         .collect(Collectors.toList());
🌐
Reddit
reddit.com › r/swiftui › does combine have an operator equivalent to java's stream::peek?
r/SwiftUI on Reddit: Does Combine have an operator equivalent to Java's Stream::peek?
February 9, 2020 -

I'd like to be able to debug my operator results step-by-step a la Java's Stream::peek method. For those unfamiliar it looks something like...

someArrayOfStrings.stream()
                  .peek(element -> print(element))
                  .map(element -> element.append("!"))
                  .peek(element -> print(element))
                  ...

and if the array is, e.g., ["hi", "i like swift"] then the output would be

hi
i like swift
hi!
i like swift!

I've just started learning how to work with Combine so this question may be misguided or flawed. I know there's the Publishers.print operator (publisher?) but I couldn't seem to get it to do what I described above. It's possible I just didn't spend enough time with it.

So after a few hours of trudging through various Combine tutorials I decided to ask here before I lose any more of my sanity :).

EDIT: I seem to have found a solution with handleEvents(receiveOutput:). I'd now like to instead ask if this is best practice.

🌐
Medium
medium.com › javarevisited › debugging-streams-with-peek-a6bb9e34d266
Debugging Streams with Peek. I blogged about Java Stream debugging… | by Shai Almog | Javarevisited | Medium
March 19, 2024 - It showcases the declarative nature ... its core, peek() is a method provided by the Stream interface, allowing developers a glance into the elements of a stream without disrupting the flow of its operations....
🌐
Medium
medium.com › @AlexanderObregon › javas-stream-peek-method-explained-0985ddcbe150
Java’s Stream.peek() Method Explained | Medium
December 23, 2024 - Debugging these transformations can sometimes feel like working with a black box. By using peek(), you can observe how data changes as it moves through the pipeline. ... import java.util.Arrays; import java.util.List; public class StreamPeekDebug ...
Find elsewhere
🌐
4Comprehension
4comprehension.com › home › idiomatic peeking with java stream api
Idiomatic Peeking with Java Stream API - { 4Comprehension }
September 27, 2018 - RTFM inspecting peek()’s user’s manual. Returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.
🌐
GeeksforGeeks
geeksforgeeks.org › java › stream-peek-method-in-java-with-examples
Stream peek() Method in Java with Examples - GeeksforGeeks
May 5, 2024 - In Java, Stream provides an powerful alternative to process data where here we will be discussing one of the very frequently used methods named peek() which being a consumer action basically returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.
🌐
Quora
quora.com › What-is-the-Peek-Method-in-Java
What is the Peek Method in Java? - Quora
The peek() method in Java is part of the Stack API. The method looks at the object at the top of this stack without removing it from the stack. This is different than pop() which returns the top element of the stack and removes it.
🌐
Codefinity
codefinity.com › courses › v2 › ceac93c7-c645-46f7-85fc-b5e9c0fbaf30 › 3cbc6bb4-77ea-45ae-ba49-6c0062f3312d › 3b7f4cc4-0595-4ea3-ba55-f829e33a2957
Learn Intermediate Processing with the peek() Method | Intermediate Operations in Stream API
123456789101112131415161718192021 package com.example; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { List<String> items = Arrays.asList("product-H31KD", "product-A12ZX", "item-X99KD", "product-B67QF", "product-12345", "invalidData"); // Example of using peek for logging and collecting filtered elements into a new list List<String> validProducts = items.stream() .peek(item -> System.out.println("Checking item: " + item)) .filter(item -> item.startsWith("product-")) .toList(); // Collecting filtered elements into a list // Printing the list of validated products System.out.println("List of validated products: " + validProducts); } } Run Code ·
🌐
OpenJDK
mail.openjdk.org › archives › list › openjfx-dev@openjdk.org › message › VKGJ3XUOREBPNRDRA4M6R52MVVYZXOAG
Re: RFR: 8273349: Check uses of Stream::peek in controls and replace as needed - openjfx-dev - openjdk.org
This PR removes potentially incorrect usages of Stream.peek(). The changed code should be covered by the tests that are already present. modules/javafx.controls/src/main/java/javafx/scene/control/ControlUtils.java line 176:
🌐
Oracle
docs.oracle.com › en › java › javase › 21 › docs › api › java.base › java › util › stream › Stream.html
Stream (Java SE 21 & JDK 21)
January 20, 2026 - Stream.of("one", "two", "three", "four") .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());
🌐
Oracle
docs.oracle.com › javase › 8 › docs › api › java › util › stream › Stream.html
Stream (Java Platform SE 8 )
2 weeks ago - Stream.of("one", "two", "three", "four") .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());
🌐
Java Guides
javaguides.net › 2024 › 04 › java-stream-peek-example.html
Java Stream peek() Example
April 27, 2024 - 1. peek() is used for debugging and is an intermediate operation that returns a stream consisting of the elements of this stream, additionally performing the provided action on each element as elements are consumed from the resulting stream.
🌐
Medium
medium.com › javarevisited › non-terminal-operation-peek-in-java8-e412407e6956
Non terminal operation peek() in Java8 | by Srikanth Dannarapu | Javarevisited | Medium
April 16, 2023 - Non terminal operation peek() in Java8 In Java 8, the peek 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 …
🌐
Medium
rameshfadatare.medium.com › java-stream-peek-method-with-examples-dacebcdf777b
Java Stream peek() Method with Examples | by Ramesh Fadatare | Medium
September 26, 2024 - Complete Java Reference: Java API Documentation. ... The peek() method allows you to inspect and perform actions on each element of the stream without modifying the elements themselves.
🌐
DebugAgent
debugagent.com › debugging-streams-with-peek
Debugging Streams with Peek
March 19, 2024 - It showcases the declarative nature ... its core, peek() is a method provided by the Stream interface, allowing developers a glance into the elements of a stream without disrupting the flow of its operations....
🌐
Oreate AI
oreateai.com › blog › indepth-analysis-and-best-practices-of-map-peek-and-foreach-methods-in-java-stream › 8b2a57f975fd46359131f485f2169e6d
In-Depth Analysis and Best Practices of Map, Peek, and forEach Methods in Java Stream - Oreate AI Blog
January 7, 2026 - This article provides an extensive analysis comparing 'map', 'peek', and 'forEach' methods in Java Streams focusing on their definitions, best practices, performance considerations while addressing...