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.
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.
HelloWorld.java
package com.sample.app.streams;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class HelloWorld {
public static void main(String args[]) {
List<String> names = Arrays.asList("Thulasi", "Ram", "Hari", "Chamu", "Tataji");
List<String> namesStartsWithTAndInUppercase = names.stream().filter(name -> name.startsWith("T"))
.map(name -> name.toUpperCase()).collect(Collectors.toList());
namesStartsWithTAndInUppercase.forEach(System.out::println);
}
}
Output
THULASI TATAJI
List<String> namesStartsWithTAndInUppercase = names
.stream()
.filter(name -> name.startsWith("T"))
.map(name -> name.toUpperCase())
.collect(Collectors.toList());
Above Java code snippet demonstrates the usage of Java streams to filter and transform a list of names.
Let's break it down.
a. stream(): This is a terminal operation that converts the names list into a stream. It marks the beginning of the stream pipeline.
b. filter(): This is an intermediate operation that filters the stream based on the given predicate. In this case, it filters names that start with the letter "T".
c. map(): This is another intermediate operation that transforms each element of the stream. Here, it converts each name to uppercase using the toUpperCase() method.
d. collect(Collectors.toList()): This is a terminal operation that collects the elements of the stream into a list. It consumes the stream and produces a new list containing the filtered and transformed elements.
So, in summary:
a. Intermediate operations: filter() and map()
b. Terminal operation: collect(Collectors.toList())
Intermediate operations will not get triggered until the terminal operation is initiated
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
In this Java code snippet, a stream pipeline is defined for processing a list of names. The pipeline includes intermediate operations such as peek() to log information about each element as it passes through the stream, filter() to select only names starting with "T", and map() to convert each name to uppercase. However, despite defining these intermediate operations, no processing occurs until a terminal operation is invoked. Only when forEach() is called to print the final elements do the intermediate operations get triggered, executing the pipeline and producing the desired output. This highlights the lazy evaluation nature of Java streams, where intermediate operations are deferred until a terminal operation initiates the stream processing.
Previous Next Home
No comments:
Post a Comment