Hey Tea Lovers! In this post, we will be talking about an unsung hero of the Java world, the Stream API which was added to Java 8 and has changed the way we do programs in java and how! It added neatness as well as made the program more readable. It helps you do functional programming in Java. Without any further ado, let us read more about this jewel.
I would be happy to connect with you guys on social media. It’s @coderstea on Twitter, Linkedin, Facebook, Instagram, and YouTube.
Please Subscribe to the newsletter to know about the latest posts from CodersTea.
What is Stream API
The Stream API, included in Java 8, is utilized for processing collections of objects. It is the flow of Objets on which various methods get applied in the pipeline to have the result. Simply put, it flows through the given Collection<Object>
and applies the different methods on the Object
to aggregate them into the desired result without affecting the original Object
.
What Stream Provides
Stream doesn’t store anything instead, it operates on the given Collection
, Array
or I/O (yes you can use it on I/O) Without changing the original data, it applies the methods to the data. Lazy evaluation helps to add multiple intermediate operations without actually utilizing them at the moment of building it. It gets evaluated only after our terminal operation. The Stream API also adds neatness to the code to make it more readable. And yes, Functional Programming is a key feature.
Prerequisites to Start Using Stream API
Stream allows you to do functional programming in Java. This means you should be comfortable with functional interfaces and lambda. Don’t worry, You just need the basics of it, which you can do with the help of this post, “Be More Functional With Java’s Functional Interfaces”. If you know these well, nobody will stop you from implementing the Stream APIFor the examples used in this post, you can find the code on GitHub here or the full project here. And yes, fill your cup of tea, start sipping and see more about this hero.
How to use Stream API
We can do this in multiple ways. Such magic tricks are,
- Collection’s
stream()
andparallelStream()
methods - Via
Arrays.stream(Object[])
- One of Stream’s static factory methods such as,
Stream.of(Object[])
IntStream.range(int, int)
Stream.iterate(Object, UnaryOperator)
- Streaming the line of a file by
BufferedReader.lines()
- Streams of random numbers can be obtained from
Random.ints()
1st and 2nd are what we use most often. For our discussion, we will be sticking to the Collection’s stream method. For Example,
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
list.stream()
// multiplying each number by 2
.map(n -> n * 2)
//taking forward only even numbers
.filter(n -> n % 2 == 0)
//printing each even number
.forEach(n -> System.out.println(n));
Code language: PHP (php)
Stream Pipeline Operations
We can perform two operations on a Stream pipeline
- Intermediate Operation
- Terminal Operation
Intermediate Operation transforms or filters the object in the preceding pipeline. These methods are nothing but the functional interfaces we discussed in our previous post. You should get familiar with them to easily understand this operation without any trouble. Stream won’t execute intermediate operations unless a Terminal Operation gets called.
Terminal Operation is an indication for the Stream to start the pipeline. Once we call the terminal operation pipeline ends and it returns the desired result. After the terminal operation, we can’t use an intermediate operation since the pipeline has ended. It can only be called once at the end of the pipeline. However, you can again start the stream if the terminal operation returns a collection. Which I highly oppose doing.
You can pretty much everything inside a single stream pipeline only.
Now, let us see these operations one by one.
Stream API Intermediate Operations
In the Stream pipeline, we can have multiple intermediate operations that transform or filter the data which will be flowing toward the next pipe without affecting the actual data on the Collection. These operations return the Stream of the desired result instead of the actual result i.e Stream<T>
. With this, we can add more intermediate operations without breaking the Stream pipeline. We will be talking about the map
and filter
in this post and others in another part. map
and filter
are the ones you will be needing most of the time.
These methods are applied one by one to the elements in the pipeline and not all at once.
Stream API map()
Think of it as a transforming function, which transforms the given object into another. It accepts a Function<T> as a parameter, which takes the element in the stream as input, performs some operation, we tell it to do, and returns Stream the resulting object. Learn more about Function
it here.
If we look at our previous example, it is multiplying each number by 2. We can have another map that can add 2 to the multiplied number and then another one to convert it to another object. and then another one and so on, you get the idea.
list.stream()
// 1. multiplying each number by 2
.map(n -> n * 2)
// 2. Converting to string.
.map(n -> "coderstea.in-Post No : " + n)
//3. Now you will be working on the
//stream of String
//4. we add another string to the previous
.map(string -> string + ". Another String")
//5. print the end result
.forEach(System.out::println);
Code language: PHP (php)
In the above example look at how we transformed a list of Integer
to the String
. Let’s break down it.
- Multiplying each number by 2. So 1 becomes 2, 4 becomes 8
- We have attached the number to a string resulting in a string object.
- The previous map was made the
Stream<Integer>
toStream<String>
due to the return type of string. - We again attached another string to perform some operations.
- Finally printing the results. Which looks like,
Output:
coderstea.in-Post No : 2. Another String
coderstea.in-Post No : 4. Another String
coderstea.in-Post No : 6. Another String
coderstea.in-Post No : 8. Another String
coderstea.in-Post No : 10. Another String
coderstea.in-Post No : 12. Another String
coderstea.in-Post No : 14. Another String
coderstea.in-Post No : 16. Another String
coderstea.in-Post No : 18. Another String
coderstea.in-Post No : 20. Another String
Code language: JavaScript (javascript)
Stream API filter()
The filter, as the name suggests, filters out the elements before it reaches the next pipe. Simply put, if the element satisfies the condition it allows it to pass through otherwise it won’t allow for the next operation. It takes an Predicate
as a parameter. Know about Predicate here.
long count = list.stream()
// 1. Need only even numbers
.filter(n -> n % 2 == 0)
// 2. Checking if the Number is
// greater than 5
.filter(n -> n > 5)
//3. multiplying the number by 5
// just to show how we can use multiple
// Intermediate operation together
.map(n -> n * 5)
// 4. Any number less than 50
.filter(n -> n < 50)
// Counting how much elements
// survived teh pipeline
.count();
System.out.println("Total " + count + " numbers survived the storm");
Code language: PHP (php)
God, so many filters. let’s break this down, shall we?
- Take only even numbers ahead.
- Forward only those numbers which are greater than 5
- Multiply the number by 5. Again, just to show you can have multiple Intermediate operations in a single Stream pipeline.
- Allow numbers that are less than 50.
- Finally,
count
(Terminal Operation) how many survived after this much filtering?
Output:
Total 2 numbers survived the storm
Stream API Terminal Operation
Till now we have seen the operations which perform something in the pipe and return the stream. In the end, we will be needing a final result, which is not a stream, don’t we? That’s where Terminal Operation comes in. It does not only give the desired result but also starts the Stream. As I said, Stream is lazy, it won’t do anything, no matter how many intermediate operations we add to the pipeline, until and unless it sees its end, the terminal operation.
You cant use the same stream after it was closed by a terminal operation. Otherwise, it will throw IllegalStateException: stream has already been operated upon or closed. You have to start a new stream
The terminal operations we will be using for our examples are forEach
and count
. Let us examine them and a few others.
Stream API forEach()
This iterates over each element passed by the preceding intermediate operation. It takes input but has a void return value. It takes Consumer
as a parameter(more on Consumer). Use this if you want to do something without returning anything. We have used it for printing the numbers.
list.stream()
// square of each number
.map(n -> n * n)
//prints each element
.forEach(n -> System.out.print(" " + n));
Code language: PHP (php)
count()
As the name suggests, it is used for counting the elements reached to the end. Like in the filter example, counting the number who satisfied all the filter’s conditions.
long evenAndLt5Count = list.stream()
// even numbers less than 5s
.filter(n -> n < 5 && n % 2 == 0)
.count();
System.out.println(evenAndLt5Count);
Code language: PHP (php)
collect()
I use this more often than any other terminal operation. It allows you to create the Collection
form of the elements of the stream after processing them. These Collection
can be List
, Set
or Map
. The Collection includes elements that passed through the last pipe.
List<Integer> processedList = list.stream()
// multiplying by 5
.map(n -> n * 5)
// numbers less than 30
.filter(n -> n < 30)
//add +2
.map(n -> n + 2)
// give the list of processed numbers
.collect(Collectors.toList());
System.out.println("processed List is \n " + processedList);
Code language: PHP (php)
In the pipeline, we multiplied the number by 5, then filtered numbers less than 30 and added 2. And after all the elements are done process, we collect them into the list.
processed List is [7, 12, 17, 22, 27]
Code language: CSS (css)
The Last Sip
We have studied what is Stream API in java, how to obtain it, how to operate it, use it. This post is kind of like an introductory post for the Stream API. There are so many diamonds in the Stream mine, which we will be mining in the next post. I highly recommend you check out “Be More Functional With Java’s Functional Interfaces” for a better understanding of the Stream API operation. You can find the code on GitHub here or the full project here.
See you in the next post. HAKUNA MATATA!!!
I would be happy to connect with you guys on social media. It’s @coderstea on Twitter, Linkedin, Facebook, Instagram, and YouTube.
Please Subscribe to the newsletter to know about the latest posts from CodersTea.