Java streams, introduced in Java 8, improvise how the collections of objects are processed. Streams represent a sequence of elements that can be processed using operations provided by the Stream API. In general, streams act as wrappers around a data source like an array, collection, or I/O channel.
How do streams streamline processing?
Stream processing entails below steps:
a. creating a stream from a data source such as collections, arrays etc.,
b. Apply a sequence of intermediate operations—like filter(), map(), and reduce()—to manipulate the elements.
c. Finally, use a terminal operation such as collect() or forEach() to yield the desired outcome.
Stream advantages
a. Conciseness: Stream operations can represent the complex data processing logic in just a few lines of code, unlike traditional loops.
b. Immutability: Streams do not modify/alter the original data source, encouraging immutability and enhancing code safety.
c. Parallelization: Stream operations have the potential to be executed in parallel, leading to better performance on processors with multiple cores.
Intermediate and terminal operations
In Java streams, operations can be categorized into two types: intermediate and terminal. Intermediate operations transform one stream into another, processing elements one by one in a lazy manner. These operations have no effect until the pipeline starts. On the other hand, terminal operations mark the end of the stream lifecycle and initiate the pipeline's execution. In a stream pipeline, composed of a source, zero or more intermediate operations, and a terminal operation, the computation is performed lazily, only when the terminal operation is invoked.
peek() method
peek() is a fundamental method offered by the Stream interface, that lets developers quickly check the elements of a stream without disrupting the flow of stream operations.
The peek() method's signature is as follows:
Stream<T> peek(Consumer<? super T> action)
It takes a Consumer functional interface as its argument, and executes an action on each element of the stream without modifying them. Logging the elements of a stream is one of its main use case for peek() method.
PeekDemo.java
package com.sample.app.streams;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
public class PeekDemo {
public static void main(String args[]) {
List<String> names = Arrays.asList("Thulasi", "Ram", "Hari", "Chamu", "Tataji");
Stream<String> namesStartsWithTAndInUppercase = names.stream()
.peek(ele -> System.out.println("Processing the element " + ele)).filter(name -> name.startsWith("T"))
.peek(ele -> System.out.println("\tElement received after applying the filter " + ele))
.map(name -> name.toUpperCase())
.peek(ele -> System.out.println("\tElement received after applying the transformation " + ele));
System.out.println("Streams is defined with intermittent operations");
namesStartsWithTAndInUppercase.forEach(ele -> System.out.println("Final element : " + ele + "\n"));
}
}
Output
Streams is defined with intermittent operations Processing the element Thulasi Element received after applying the filter Thulasi Element received after applying the transformation THULASI Final element : THULASI Processing the element Ram Processing the element Hari Processing the element Chamu Processing the element Tataji Element received after applying the filter Tataji Element received after applying the transformation TATAJI Final element : TATAJI
Adding peek() before and after the filter and map operations helps us to see which elements are being worked on and how these stream operations affects the stream. This is really useful for fixing errors, especially when the logic in the stream operations gets complicated.
No comments:
Post a Comment