Streams in Java : Introducing declarative style in Java
Collections in java are one of the most regularly used concept in our daily programming
Considering this scenario below:
There is Employee class.
Our problem statement is we have a list of Employee object and we will filter employees whose age is greater than 25 and then store their salaries in a different list
We will use List with Iterator
Before java 8, when we use to operate on collections, we usually have to implement below steps
Please refer the comments, given inside /**/ from above code snippet
- First we have to create an iterator object.
- Then check if iterator can travel (verify if collection is not null) and fetch a particular item.
- Once we get true from step 2, we can use next() to go ahead and extract the item
- After getting value from step 3, only can we do operation that we need to perform with value from collection.
To summarize, we are telling compiler step by step in order to do any operation on a collection.
This we can also refer as imperative style of programming i.e. we are telling how to do a particular operation.
- For one collection , these are minimum steps that we have to perform.
If we work with multiple collections, these steps have to be performed for all those collections as well. This will result in repetition of logic.
- When we operate on collection using an iterator, we can perform operation on elements only in sequential manner.
For collections having relatively smaller number of values, it will not be an issue.
As iterator does not enable parallel operation, for large collections, performance might become an issue if we do operation in sequential manner.
- When we are doing operations on collections, we use Iterator, hasNext(), next() then use some ‘if’ or ‘while’ loop operations , after that, if needed, filter values or do required operations on values and store our desired result in a local variable.
In programming logic, we can conclude the above set of operations with these observations below
- Parallel operations not supported, which will lead to performance issue in handling large collections
- Code is relatively less concise or multiple logic has to be written to achieve a particular result.
- Multiple local variables might be needed to declare in order to store some value for next operation or to store the result that is actually desired from collection.
- Readability is relatively less
[This is dependent on programmer. You need to understand the basics of functional programming and Java 8 concepts and start using it . If you find it useful, you will continue to use it, provided it is the best option for any particular scenario. Well, there are exceptions]
Streams in Java enabled us to overcome these constraints with a better solution.
What are Streams ?
Streams is a sequence of elements or data items created from a source of data which enables us to perform data processing operations both in sequential or in parallel manner
- Streams enables us to do computations or process the data present in stream through filtering a data or sorting data items or mapping data items or summing up values.
- For creating or generating a stream, we can do it only from a data source such as collections or arrays or I/O resources.
- We can do operations on a stream through sequential or parallel operations.
- The various operations that we can use on streams are present in Stream interface
java.util.stream.Stream
There are 2 categories of operations:
Intermediate Operations :
Produces streams and hence continue the stream pipeline which enables more methods to operate on it
Terminal operations :
Produces a final result from streams
- Internal iteration is support in stream. We do not have to declare steps for iteration while using streams.
What is stream pipeline ?
When intermediate operations work on stream, they take elements from an input stream and produce a output stream.
This is one chain of operation having two streams.
When two or more of this chains are connected , they form a stream pipeline. Streams connect through these chain of operations and form a stream pipeline.
Example of using stream
There is Employee class. Our problem statement is we have a list of Employee object and we will filter employees whose age is greater than 25 and then store their salaries in a different list.
Sequence of data items . . . isn’t it a collection then ?
- Streams are used for data processing operations whereas Collections is for storing elements, it enables us to find specific elements based on index position.
- Once we create an iterator from collections, we can use it repeatedly whereas for stream, once we call a stream method on a collection , we can iterate through it only once. It will not be accurate to say we cannot reuse stream, what we can do is whenever we require to process a stream, we can call the stream method on a collection and use it.
- In streams, we tell what we need and code accordingly. It is kind of declaring the details that needs to be done and it is taken care of without directly coding the logic of how to do it.
Basically stream supports Declarative programming whereas in collections, we directly code how to get detail that we need including declaring iterator, calling hasNext() and next() and then doing required operations if needed and then getting the desired result. Basically collection supports Imperative programming.
- Streams, especially parallel streams are useful when data set is large. Comparatively, time span for operation on streams is greater than operation using loops.
Performance comparison of Loops vs Sequential stream vs Parallel Stream
Total number of elements = 10 million
Total number of elements = 100 million
Thanks for reading !!
If you find it interesting and informative. Please click on claps 👏 on this post and share for greater reach.
Share your experiences ,ideas and feedback by commenting on this post.
Feel free to follow me at Subhodip Basu for more interesting articles.
Feel free to connect with me on LinkedIn