DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Last call! Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

Related

  • Exploring Java Stream API's peek Method
  • Linked List Popular Problems Vol. 1
  • Java: Why a Set Can Contain Duplicate Elements
  • What Is Ant, Really?

Trending

  • 5 Subtle Indicators Your Development Environment Is Under Siege
  • The Cypress Edge: Next-Level Testing Strategies for React Developers
  • Event-Driven Architectures: Designing Scalable and Resilient Cloud Solutions
  • How to Convert XLS to XLSX in Java
  1. DZone
  2. Coding
  3. Java
  4. Peeking Inside Java Streams With Stream.peek

Peeking Inside Java Streams With Stream.peek

How to use the Stream.peek(Consumer) method in Java to help visualize stream operations.

By 
Dustin Marx user avatar
Dustin Marx
·
Jun. 15, 18 · Tutorial
Likes (10)
Comment
Save
Tweet
Share
38.1K Views

Join the DZone community and get the full member experience.

Join For Free

For a Java developer that is new to JDK 8-introduced pipelines and streams, the peek(Consumer) method provided by the Stream interface can be a useful tool to help visualize how stream operations behave. Even Java developers who are more familiar with Java streams and aggregation operations may occasionally find Stream.peek(Consumer) useful for understanding the implications and interactions of complex intermediate stream operations.

The Stream.peek(Consumer) method expects a Consumer, which is essentially a block of code that accepts a single argument and returns nothing. The peek(Consumer) method returns the same elements of the stream that were passed to it, so there will be no changes to the contents of the stream unless the block of code passed to the peek(Consumer) method mutates the objects in the stream. It's likely that the vast majority of the uses of Stream.peek(Consumer) are read-only printing of the contents of the objects in the stream at the time of invocation of that method.

The Javadoc-based API documentation for Stream.peek(Consumer) explains this method's behaviors in some detail and provides an example of its usage. That example is slightly adapted in the following code:

final List<String> strings  
   = Stream.of("one", "two", "three", "four")  
      .peek(e-> out.println("Original Element: " + e))  
      .filter(e -> e.length() > 3)  
      .peek(e -> out.println("Filtered value: " + e))  
      .map(String::toUpperCase)  
      .peek(e -> out.println("Mapped value: " + e))  
      .collect(Collectors.toList());  
out.println("Final Results: " + strings);  


When the above code is executed, its associated output looks something like this:

Original Element: one
Original Element: two
Original Element: three
Filtered value: three
Mapped value: THREE
Original Element: four
Filtered value: four
Mapped value: FOUR
Final Results: [THREE, FOUR]


The output tells the story of the stream operations' work on the elements provided to them. The first invocation of the intermediate peek operation will write each element in the original stream out to the system output with the prefix "Original Element." Instances of the intermediate peek operation that occur later are not executed for every original String , because each of these peek operations occurs after at least once filtering has taken place.

The peek-enabled output also clearly shows the results of executing the intermediate operation map on each String element to its upper case equivalent. The collect operation is a terminating operation, and, therefore, no peek is placed after that. Strategic placement of peek operations provides significant insight into the stream processing that takes place.

The Javadoc for Stream.peek(Consumer) states that "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 is exactly what the example and output shown above demonstrate and is likely the most common application of Stream.peek(Consumer).

Stream.peek(Consumer)'s Javadoc documentation starts with this descriptive sentence, "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." In the previous example, the action performed on each element, as it was consumed, was merely to write its string representation to standard output. However, the action taken can be anything that can be specified as a Consumer,which is any code block accepting a single argument and returning no arguments. The next example demonstrates how peek(Consumer) can even be used to change contents of objects on the stream.

In the first example in this post, peek(Consumer) could not change the stream elements because those elements were Java Strings, which are immutable. However, if the stream elements are mutable, the Consumer passed to peek(Consumer) can alter the contents of those elements. To illustrate this, I'll use the simple class MutablePerson shown, which is shown below.

MutablePerson.java

package dustin.examples.jdk8.streams;  

/** 
 * Represents person whose name can be changed. 
 */  
public class MutablePerson  
{  
   private String name;  

   public MutablePerson(final String newName)  
   {  
      name = newName;  
   }  

   public String getName()  
   {  
      return name;  
   }  

   public void setName(final String newName)  
   {  
      name = newName;  
   }  

   @Override  
   public String toString()  
   {  
      return name;  
   }  
}  


The next code listing shows how Stream.peek(Consumer) can change the results of the stream operation when the elements in that stream are mutable.

final List<MutablePerson> people  
   = Stream.of(  
      new MutablePerson("Fred"),  
      new MutablePerson("Wilma"),  
      new MutablePerson("Barney"),  
      new MutablePerson("Betty"))  
   .peek(person -> out.println(person))  
   .peek(person -> person.setName(person.getName().toUpperCase()))  
   .collect(Collectors.toList());  
out.println("People: " + people);  


When the above code is executed, it produces output that looks like this:

Fred
Wilma
Barney
Betty
People: [FRED, WILMA, BARNEY, BETTY]


This example shows that the Consumer passed to peek did change the case of the peoples' names to all uppercase. This was only possible because the objects being processed are mutable. Some have argued that using peek to mutate the elements in a stream might be an antipattern. However, I find myself uncomfortable with this approach, but I also generally don't like having methods' arguments be "output parameters." The name of the peek method advertises one's just looking (and not touching), but the Consumer argument it accepts advertises that something could be changed (Consumer's Javadoc states, "Unlike most other functional interfaces, Consumer is expected to operate via side-effects"). The blog post "Idiomatic Peeking with Java Stream API" further discusses the potential issues associated with using Stream.peek(Consumer) with mutating operations.

Steam.peek(Consumer) is a useful tool for understanding how stream operations are impacting elements.



If you enjoyed this article and want to learn more about Java Streams, check out this collection of tutorials and articles on all things Java Streams.

Stream processing Java (programming language) Element

Published at DZone with permission of Dustin Marx, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Exploring Java Stream API's peek Method
  • Linked List Popular Problems Vol. 1
  • Java: Why a Set Can Contain Duplicate Elements
  • What Is Ant, Really?

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!