A stream is an ordered pipeline of aggregate operations(filter(), map(), forEach(), and collect()) that process a (conceptually unbounded) sequence of elements. A stream pipeline consists of a source, followed by zero or more intermediate operations; and a terminal operation. An aggregate operation performs a function. Ideally, a function’s output in a stream depends only on its input arguments. A stream holds no non-transient stage. Every stream works essentially the same which is:
- Starts with a source of data.
- Processes the data through a pipeline of intermediate operations.
- Finishes with a terminal operation.
By default, each aggregate operation in a stream runs its function sequentially, i.e., one after another in the caller’s thread of control. Streams can be created from Collections, Lists, Sets, ints, longs, doubles, arrays, lines of a file.
Stream operations are either intermediate or terminal. Intermediate operations such as filter, map, or sort return a stream, so we can chain multiple intermediate operations. Terminal operations such as forEach, collect, or reduce are either void or return a non-stream result. Streams bring functional programming in Java and are supported starting in Java 8. The Java 8 stream is an implementation of the PQSA1 Pipes and Filter pattern.
Example:
Java
// Java Program to Compare Streams to Loops // Importing required libraries import java.io.IOException; import java.lang.String; import java.nio.file.*; import java.util.*; import java.util.Arrays; import java.util.List; import java.util.stream.*; // Main class // JavaStreams class GFG { // Main driver method public static void main(String[] args) throws IOException { // 1. Integer Stream System.out.println( "Integer Stream : " ); IntStream.range( 1 , 10 ).forEach(System.out::print); // New line System.out.println(); // 2. Integer Stream with skip System.out.println( "Integer Stream with skip : " ); IntStream.range( 1 , 10 ).skip( 5 ).forEach( x -> System.out.println(x)); // New line System.out.println(); // 3. Integer Stream with sum System.out.println( "Integer Stream with sum : " ); System.out.println(IntStream.range( 1 , 5 ).sum()); // New line System.out.println(); // 4. Stream.of, sorted and findFirst System.out.println( "Stream.of, sorted and findFirst : " ); Stream.of( "Java " , "Scala " , "Ruby " ) .sorted() .findFirst() .ifPresent(System.out::println); // New line System.out.println(); // 5. Stream from Array, sort, filter and print String[] names = { "AI" , "Matlab" , "Scikit" , "TensorFlow" , "OpenCV" , "DeepLearning" , "NLP" , "NeuralNetworks" , "Regression" }; System.out.println( "Stream from Array, sort, filter and print : " ); Arrays .stream(names) // same as Stream.of(names) .filter(x -> x.startsWith( "S" )) .sorted() .forEach(System.out::println); // New line System.out.println(); // 6. average of squares of an int array System.out.println( "Average of squares of an int array : " ); Arrays.stream( new int [] { 2 , 4 , 6 , 8 , 10 }) .map(x -> x * x) .average() .ifPresent(System.out::println); // New line System.out.println(); // 7. Stream from List, filter and print // Display message only System.out.println( "Stream from List, filter and print : " ); List<String> people = Arrays.asList( "AI" , "Matlab" , "Scikit" , "TensorFlow" , "OpenCV" , "DeepLearning" , "NLP" , "NeuralNetworks" ); people.stream() .map(String::toLowerCase) .filter(x -> x.startsWith( "a" )) .forEach(System.out::println); // New line System.out.println(); // 8. Reduction - sum // Display message only System.out.println( "Reduction - sum : " ); double total = Stream.of( 7.3 , 1.5 , 4.8 ) .reduce( 0.0 , (Double a, Double b) -> a + b); // Print and display System.out.println( "Total = " + total); System.out.println(); // 9. Reduction - summary statistics System.out.println( "Reduction - summary statistics : " ); IntSummaryStatistics summary = IntStream.of( 7 , 2 , 19 , 88 , 73 , 4 , 10 ) .summaryStatistics(); // Print and display System.out.println(summary); System.out.println(); } } |
Integer Stream : 123456789 Integer Stream with skip : 6 7 8 9 Integer Stream with sum : 10 Stream.of, sorted and findFirst : Java Stream from Array, sort, filter and print : Scikit Average of squares of an int array : 44.0 Stream from List, filter and print : ai Reduction - sum : Total = 13.600000000000001 Reduction - summary statistics : IntSummaryStatistics{count=7, sum=203, min=2, average=29.000000, max=88}
Now, discussing loops in order to figure out in order to land onto conclusive differences. Looping is a feature in Java that facilitates the execution of a set of instructions until the controlling Boolean expression evaluates to false. Different types of loops are provided to fit any programming need. Each loop has its own purpose and a suitable use case to serve.
Example:
Java
// Java Program to Comparing Streams to Loops // Importing utility packages import java.util.*; // Class 1 // helper class class ProgrammingLanguage { // Member variables of this class int rank; String name; int value; // Member method of this class public ProgrammingLanguage( int rank, String name, int value) { // this keyword is used to refer current object // itself this .rank = rank; this .name = name; this .value = value; } } // Class 2 // JavaStreamExample public class GFG { // MAin driver method public static void main(String[] args) { // Creating an object of List class // Declaring object of user defined type (above // class) List<ProgrammingLanguage> programmingLanguage = new ArrayList<ProgrammingLanguage>(); // Adding elements to the object of this class // Custom input entries programmingLanguage.add( new ProgrammingLanguage( 1 , "Java" , 7000 )); programmingLanguage.add( new ProgrammingLanguage( 2 , "Rust" , 2000 )); programmingLanguage.add( new ProgrammingLanguage( 3 , "Ruby" , 1500 )); programmingLanguage.add( new ProgrammingLanguage( 4 , "Scala" , 2500 )); programmingLanguage.add( new ProgrammingLanguage( 5 , "Groovy" , 4000 )); // Creating object of List class of integer type List<Integer> languageValueList = new ArrayList<Integer>(); // For each loops for iteration for (ProgrammingLanguage language : programmingLanguage) { // Filtering data of List if (language.value < 3000 ) { // Adding price to above elements languageValueList.add(language.value); } } // Print and display all elements inside the object System.out.println(languageValueList); } } |
[2000, 1500, 2500]
By far we have understood both of the concepts and come across to know they don’t go hand in hand, one has advantage over other as per the usage where it is to be used. Hence, wrapping off the article by illustrating their advantages which are as follows:
Advantages of Streams
- Streams are a more declarative style. Or a more expressive style.
- Streams have a strong affinity with functions. Java 8 introduces lambdas and functional interfaces, which opens a whole toolbox of powerful techniques. Streams provide the most convenient and natural way to apply functions to sequences of objects.
- Streams encourage less mutability. This is sort of related to the functional programming aspect i.e., the kind of programs we write using streams tend to be the kind of programs where we don’t modify objects.
- Streams encourage loose coupling. Our stream-handling code doesn’t need to know the source of the stream or its eventual terminating method.
- Streams can succinctly express quite sophisticated behavior.
Advantages of Loops
- Performance: A for loop through an array is extremely lightweight both in terms of heap and CPU usage. If raw speed and memory thriftiness is a priority, using a stream is worse.
- Familiarity: The world is full of experienced procedural programmers, from many language backgrounds, for whom loops are familiar and streams are novel. In some environments, you want to write code that’s familiar to that kind of person.
- Cognitive overhead: Because of its declarative nature, and increased abstraction from what’s happening underneath, you may need to build a new mental model of how code relates to execution. Actually, you only need to do this when things go wrong, or if you need to deeply analyze performance or subtle bugs. When it “just works”, it just works.
- Debuggers are improving, but even now, when we are stepping through stream code in a debugger, it can be harder work than the equivalent loop, because a simple loop is very close to the variables and code locations that a traditional debugger works with.
Conclusion:
If you have a small list; for loops perform better, if you have a huge list; a parallel stream will perform better. And since parallel streams have quite a bit of overhead, it is not advised to use these unless you are sure it is worth the overhead.