Guide to Stream API in Java

Guide to Stream API in Java

This tutorial will help you understand everything about stream API in Java

In Java 8 Stream API was introduced, Stream API is used to PROCESS the collection of objects.
It'd be better if you have a little know-how of lambda expressions.
I've already published 2 articles on lambda expression you can refer to those if you are new to lambda expression.
Part 1
Part 2
Now let us first understand the difference between collections and streams.

If we want to present a group of objects as a single entity, then go for collections.
If we want to process objects from the collection, then we should go for streams.

Note- Stream is not a data structure, so it does not store data, but it operates on the source data structure i.e collections, arrays, etc.

If you are already aware of I/O Stream, are IOStream and Streams API the same?
No..

I/O Stream - talks about the stream of data.
Streams - talks about the stream of objects.

If this is clear till now, let's move forward.

1) How to create a stream?

a) Stream of Collection - Using stream() method

As we discussed, to create a stream we need a collection of objects so let us create an ArrayList.

List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));
Stream<Integer> stream = list.stream();

We can use the stream() method of the Stream interface

b) Stream of Array - Using stream()

Arrays can also be used as a source for a stream.

Integer[] arr = {1,2,3,4,5,6};
Stream<Integer> streamOfArray = Arrays.stream(arr);
Stream<Integer> streamOfPart = Arrays.stream(arr, 2, 5);

Using the Arrays.stream() method we can create a stream of a whole array or part of the array.

2) How to process streams?

Now that we have created streams of objects, as we earlier discussed streams are used to process the objects, now let's focus on the processing of objects.

a) filter

If we want to filter out some objects from the collections of objects based on some condition, we can use filter() method of the Stream Interface.

List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));
Stream<Integer> stream = list.stream();
List<Integer> listAfterFilter = stream.filter(i -> i % 2 == 0).collect(Collectors.toList());
System.out.println(listAfterFilter);//[2, 4, 6]

We want some conditions to filter right?

So, filter() method takes Predicate as an argument, as we know Predicate is a Functional Interface so we can use a lambda expression.
In the above example, we are filtering all the even elements from the list, after filtering we are collecting the new list using the collect() method.

b) map

If the requirement is to square all elements of the ArrayList, we can use the map.

Basically, if we want to perform some function on every object of the stream, we should use a map().

List<Integer> listAfterMap = stream.map(i->i*i).collect(Collectors.toList());
System.out.println(listAfterMap);//[1, 4, 9, 16, 25, 36]

c) count

count() is used to get a number of elements in the stream.

List<Integer> list = new ArrayList<>(Arrays.asList(1,2,3,4,5,6));
Stream<Integer> stream = list.stream();
//to get number of even elements
long countEven = stream.filter(i -> i % 2 == 0).count();
System.out.println(countEven);//3

d) sorted

sorted() method of the Stream interface is used to sort all objects

List<Integer> list = new ArrayList<>(Arrays.asList(4,5,6,1,2,3));
Stream<Integer> stream = list.stream();
List<Integer> sortedList = stream.sorted().collect(Collectors.toList());
System.out.println(sortedList);//[1, 2, 3, 4, 5, 6]

By default sorted() method sorts in increasing order
What if we want to do customized sorting?
Let's say descending order
We need to pass an instance of comparator as an argument to the sorted method.

Now,let's talk about Comparator interface.
Comparator is a Functional Interface that has a method compare() which actually does a comparison between two objects.

image.png

compare(o1,o2)
--> returns -ve if o1 has to come before o2
--> returns +ve if o1 has to come after o2
--> returns 0 if o1 and o2 are equal

So, to sort in descending order, we need to pass a lambda expression so the sorted method.

(i1,i2)->(i1<i2)? 1 : (i1>i2) ? -1 : 0

We want to sort in descending order
If i1 is less than i2, means i1 should come after i2, so return 1
If i1 is greater than i2, means i1 should come before i2, so return -1
If both are equal return 0

List<Integer> list = new ArrayList<>(Arrays.asList(4,5,6,1,2,3));
Stream<Integer> stream = list.stream();
List<Integer> sortedList = stream.sorted((i1,i2)->(i1<i2)? 1 : (i1>i2) ? -1 : 0).collect(Collectors.toList());
System.out.println(sortedList);//[6, 5, 4, 3, 2, 1]

3) Iterating the stream

To iterate the collection of objects we generally use for or for-each loop.

List<Integer> list = new ArrayList<>(Arrays.asList(4,5,6,1,2,3));
for(Integer i : list){
    System.out.println(i);
 }

But using streams we can avoid this, instead, we can use the forEach method(not loop).
This method always expects an instance of a Consumer interface, and as we know Consumer is a functional interface and for a functional interface, we can use lambda expressions.

List<Integer> list = new ArrayList<>(Arrays.asList(4,5,6,1,2,3));
Stream<Integer> stream = list.stream();
stream.forEach(i->System.out.println(i));

So, as in the above example, we have printed each element of the stream, we can also pass any implementation of the consumer interface.

Conclusion

In this article, we have seen how to create streams, we then understood how to use different methods of the Stream interface to process the objects like filter(), map(), sorted(), count().
There are many more methods you can refer to the official documentation for more details.
This is it for this article.
Do like this, if you find it helpful.
If any suggestions, do mention them in the comments.

Thanks for Reading !!
Keep Learning !!