Monday, April 3, 2023

10 Examples of Stream Class in Java 8 [ map + filter+ flatmap + collect + findFirst ]

There is no doubt that Stream class is one of the most important class of Java SE 8 API and a solid knowledge of this class is needed to write modern day Java code. There is no excuse to write the Java code we used to write in imperative style like using loops and not using functional programming idioms. In fact, now it's one of the best practice in Java to favor functional programming and immutable data structure over imperative programming. More and more Technical Leads are enforcing developers to learn Java 8 and write better code leveraging improved API. The Java designers has done a fantastic job to convert any collection to Stream by adding stream() method into Collection interface. 

Once you get the Stream with elements you can apply most of the functional programming concepts e.g. map, filter, flatMap, reduce easily. There are tons of such methods into Stream class and in this article, I am going to explain the most important one. 

Remember the 80/20 rule of Ranga Karnam, author of famous Spring Masterclass course on Udemy, who focus on teaching the 20% important stuff which we happen to use 80% of time. I am following some of his advice in this article and instead of explaining every single method of Stream class, I have chose the most important ones which Java developers will most likely use in their day to day life. 


How to Learn Stream API by Examples in Java

Here is a list of methods from Stream class we'll explore in this article:

we'll see a brief description of the method like what it does and when to use it followed by a simple example which you can remember. 


1. Stream.map() Example

The map() function is used to transform Stream of one type of object to Stream of another type of object. It accept a method which it can apply on each element of Stream to transform the object. For example, by using map() method you can convert a Stream of Integer to Stream of String by applying the valueOf() method on each element. 

List<Integer> listOfIntegers = input.stream()
.map(Integer::valueOf)
.collect(Collectors.toList());

System.out.println("list of string: " + input);
System.out.println("list of integer using Stream.map(): " + listOfIntegers);

Output: 
list of string: [1, 20, 300, 4000, 5000, 60000]
list of integer using Stream.map(): [1, 20, 300, 4000, 5000, 60000]

You can see that we have successfully converted list of String to list of Integer by applying Integer.valueOf() method to each element using map() function. 



2. Stream.filter() Example

As the name suggests, the filter method can filter elements in Stream, but it works more like select then filter. You provide a boolean condition, which is applied to all the elements and only those passed the conditions are present in new Stream. The condition is specified using Predicate interface, which is a functional interface and represent a function which accepts a type and return a Boolean. 

here is an example of filter() method of Stream class to select String whose length is greater than two:

List<String> listOfStringWithLengthGreaterThanTwo = input.stream()
                         .filter(s->> s.length() > 2)
                         .collect(Collectors.toList()); 
System.out.println("original list of string: " + input);
System.out.println("filtered list of String with length greater than 2 using
Stream.map(): " + listOfStringWithLengthGreaterThanTwo);

Output:
original list of string: [1, 20, 300, 4000, 5000, 60000]
filtered list of String with length greater than 2 using Stream.map(): 
[300, 4000, 5000, 60000]

You can see that only String with length greater than two are present in the filtered list. String e.g. "1", "20" which has length two or less than two were filtered out.

10 Examples of Stream Class in Java 8 [ map + filter+ flatmap  + collect + findFirst ]



3. Stream.flatMap() Example

The flatmap() function works like map i.e. it transform one object to another but in addition to transformation it also used for flattening the Stream. For example if you have a list of list then you can convert it into just a big list by using flatMap() function. 

Here is an example of flattening a list of list of integers into just a list of integers" .

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

List<Integer> flattenedList = unflattened.stream()
                                         .flatMap(list->> list.stream())
                                         .collect(Collectors.toList());
System.out.println("unflattend list : " + unflattened);
System.out.println("flattend list : " + flattenedList);

Output:
unflattend list : [[1, 3, 5], [2, 4, 6]]
flattend list : [1, 3, 5, 2, 4, 6]

You can see that flattened list contains all the elements from individual list. You can also provide a transformation function just like we did to map but I leave that to you. If you need some assistance you can see these examples of flatMap function for more details. 


4. Stream.toArray() Example

The toArray() method of Stream class can be used to convert a Stream into an array in Java. One of the challenge converting list to array was the type of array as they don't support generics. But, with Stream you don't need to worry, you can specify a Supplier as constructor reference on toArray() method to accumulate elements in the right kind of array. 

here is an example of converting a Stream of String into an String array in in Java:

String[] array = input.stream().toArray(String[]::new);
System.out.println("array created using Stream.toArray(): " + array);

Output:
array created using Stream.toArray(): [Ljava.lang.String;@3d075dc0

The output is not pretty because we have directly printed the array. If you want a more meaningful value, use the Arrays.toString(array) which will print the elements of array instead of hashcode. 



5. Stream.distict() Example

The distinct method of Stream class is like the distinct keyword of SQL, both removes duplicates. You can use distinct() method to get only unique values form Stream. 

here is an example of collecting unique values of a Stream into a List in Java 8 using Stream.distinct() method:

List<String> withoutDupes = input.stream()
                                 .distinct()
                                 .collect(Collectors.toList());
System.out.println("original list of string: " + input);
System.out.println("list of string wihtout dupliates using Stream.distinct(): " 
 + withoutDupes);

Output:
original list of string: [1, 20, 300, 4000, 5000, 60000]
list of string wihtout dupliates using Stream.distinct(): 
[1, 20, 300, 4000, 5000, 60000]

Since, our list didn't contain any duplicate the number of elements in both original and processed list is same. Just add a duplicate in the original list and see how that is removed using distinct() method. 



6. Stream.sorted() Example

The sorted() method of Stream class can be used to sort a Stream in Java. Since Stream keeps the element in the order, it's possible to sort them on any custom order specified by Comparator. 

Here is an example of sorting a Stream in the reverse order:

List<String> sortedInDescendingOrder = input.stream()
                                    .sorted(Comparator.reverseOrder())
                                    .collect(Collectors.toList());
System.out.println("original list of string: " + input);
System.out.println("sorted list of String using Stream.sorted() : " 
                                + sortedInDescendingOrder);

Output:
original list of string: [1, 20, 300, 4000, 5000, 60000]
sorted list of String using Stream.sorted() : [60000, 5000, 4000, 300, 20, 1]

You can see that elements of Stream.sorted() are printed in opposite order than it was in the original list.


7. Stream.peek() Example

The peek() method of Stream class can be a useful tool for debugging a Stream pipeline and lambda expression. You can use the peek() to find out what happening in each stage of Stream processing pipeline. 

For example, in following code, we have used peek() to print the element after filter but in the output we only see 300 and not other values. This happens because Stream is lazy and since the terminal method is findFirst() only the first element whose length is greater than 2 is returned. 

String result = input.stream()
.filter(s->> s.length() > 2)
.peek(System.out::println)
.findFirst().orElse("");

System.out.println("filtered list: " + result);

Output:
300
filtered list: 300 

By using peek() in the above example, we can confirm that Stream processing is lazy. The filter method didn't filter all object of Stream instead, processing only lasted until the answer is found. 


8. Steram.of() Example

The of() is a static factory method of Stream class which can be used to create a Stream of arbitrary values. In most of the cases, you will get Stream from Collection e.g. list or set but in some cases you may need to create Stream yourself, and Stream.of() is perfect for those cases. 

The method is overloaded to create Stream of one, two, three, or more object in Java. The last version accepts a variable arguments which allows you to create Stream or any number of elements.

Here is an example of Stream.of() method in Java:

Stream<Integer> streamOfPrimeNumbers = Stream.of(2, 3, 5, 7, 11, 13, 17);
System.out.println("stream of prime numbers: " + streamOfPrimeNumbers);

Output:

stream of prime numbers: java.util.stream.ReferencePipeline$Head@58372a00

You can see that the output is not very useful because unlike Collection class, Stream doesn't seem to override the toString() method. 


9. Stream.collect() Example

The collect() method is one of the most important method of Stream class. It provides the bridge between Stream and Collection. You can use collect to convert a Stream to a List, Set, Map, or any other Collection in Java. 

It accepts a Collector which can be used to accumulate objects of Stream into specified Collection. The Collectors class provides several utility method to convert Stream to list, set, and map. 

Here is an example to convert a Stream of String into an ArrayList of Integer in Java 8:
ArrayList<Integer> arrayListOfIntegers = input.stream()
                           .map(Integer::valueOf)
                           .collect(Collectors.toCollection(ArrayList::new));
System.out.println("original list of string: " + input);
System.out.println("ArrayList of Integers using Stream.collect(): " 
 + arrayListOfIntegers);

Output:
original list of string: [1, 20, 300, 4000, 5000, 60000]
ArrayList of Integers using Stream.collect(): [1, 20, 300, 4000, 5000, 60000]

You may see that output of both the list is same because we are eventually printing the String but first list is a of Sting objects while ArrayList contains Integer objects. 


10. Stream.findFirst() Example

The findFirst() method of Stream class return the first element of Stream. Precisely it return an Optional because it's possible that Stream may be empty and there won't be any element on it. 

Here is an example of findFirst() method which either return first element or an empty String:

String firstStringFromStream = input.stream().findFirst().orElse("");
System.out.println("original list of string: " + input);
System.out.println("first element from Stream: " + firstStringFromStream);

Output:
original list of string: [1, 20, 300, 4000, 5000, 60000]
first element from Stream: 1

You can see that the first element of Stream has returned. You can further try this method on an empty Stream and see what happens.


Java Program to Learn Stream API by Examples 

Here is our complete Java program to demonstrate how to use various methods of Stream class in Java 8. This program contains all above examples and you can just copy paste and run it on Eclipse Java IDE

You don't even need to create a Java class, just copy paste it in your Java project and Eclipse will take care of it. You can also play around by changing the input and trying with different type of List. 

package tool;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;



/**
* 
* A simple Java Program to convert a String to 
* short primitive or Short wrapper object in Java.
*/
public class Hello {

public static void main(String[] args) {

// input-> a list of String object
List<String> input = Arrays.asList("1", "20", "300", "4000", "5000", "60000");


//1. Stream.map() Example
List<Integer> listOfIntegers 
= input.stream().map(Integer::valueOf).collect(Collectors.toList());
System.out.println("list of string: " + input);
System.out.println("list of integer using Stream.map(): " + listOfIntegers);


//2. Stream.filter() Example
List<String> listOfStringWithLengthGreaterThanTwo 
= input.stream().filter(s->> s.length() > 2).collect(Collectors.toList()); 
System.out.println("original list of string: " + input);
System.out.println("filtered list of String with length greater than 2 using Stream.map():
 " + listOfStringWithLengthGreaterThanTwo);


//3. Stream.flatMap() Example
List<List<Integer>> unflattened = new ArrayList<>();
unflattened.add(Arrays.asList(1, 3, 5));
unflattened.add(Arrays.asList(2, 4, 6));

List<Integer> flattenedList 
= unflattened.stream().flatMap(list->> list.stream()).collect(Collectors.toList());
System.out.println("unflattend list : " + unflattened);
System.out.println("flattend list : " + flattenedList);


//4. Stream.toArray() Example
String[] array = input.stream().toArray(String[]::new);
System.out.println("array created using Stream.toArray(): " + array);


//5. Stream.distict() Example
List<String> withoutDupes = input.stream().distinct().collect(Collectors.toList());
System.out.println("original list of string: " + input);
System.out.println("list of string wihtout dupliates using Stream.distinct(): " 
+ withoutDupes);


//6. Stream.sorted() Example
List<String> sortedInDescendingOrder 
= input.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
System.out.println("original list of string: " + input);
System.out.println("sorted list of String using Stream.sorted() : " 
+ sortedInDescendingOrder);


//7. Stream.findFirst() Example
String firstStringFromStream = input.stream().findFirst().orElse("");
System.out.println("original list of string: " + input);
System.out.println("first element from Stream: " + firstStringFromStream);


//8. Steram.of() Example
Stream<Integer> streamOfPrimeNumbers = Stream.of(2, 3, 5, 7, 11, 13, 17);
System.out.println("stream of prime numbers: " + streamOfPrimeNumbers);


//9. Stream.collect() Example
ArrayList<Integer> arrayListOfIntegers 
= input.stream().map(Integer::valueOf).collect(Collectors.toCollection(ArrayList::new));
System.out.println("original list of string: " + input);
System.out.println("ArrayList of Integers using Stream.collect(): " 
+ arrayListOfIntegers);


//10. Stream.peek() Example
String result = input.stream()
.filter(s->> s.length() > 2)
.peek(System.out::println)
.findFirst().orElse("");

System.out.println("filtered list: " + result);
}

}

Output:
list of string: [1, 20, 300, 4000, 5000, 60000]
list of integer using Stream.map(): [1, 20, 300, 4000, 5000, 60000]
original list of string: [1, 20, 300, 4000, 5000, 60000]
filtered list of String with length greater than 2 using Stream.map():
 [300, 4000, 5000, 60000]
unflattend list : [[1, 3, 5], [2, 4, 6]]
flattend list : [1, 3, 5, 2, 4, 6]
array created using Stream.toArray(): [Ljava.lang.String;@3d075dc0
original list of string: [1, 20, 300, 4000, 5000, 60000]
list of string wihtout dupliates using Stream.distinct(): 
[1, 20, 300, 4000, 5000, 60000]
original list of string: [1, 20, 300, 4000, 5000, 60000]
sorted list of String using Stream.sorted() : [60000, 5000, 4000, 300, 20, 1]
original list of string: [1, 20, 300, 4000, 5000, 60000]
first element from Stream: 1
stream of prime numbers: java.util.stream.ReferencePipeline$Head@58372a00
original list of string: [1, 20, 300, 4000, 5000, 60000]
ArrayList of Integers using Stream.collect(): [1, 20, 300, 4000, 5000, 60000]
300
filtered list: 300


Important Points about Stream class in Java 8

Now that you have seen some of the essential methods of Stream class and learned how and when to use them, it's time to revise whatever you have learned so far. 

1) Use map() method to transform Stream of one type to another. 

2) Use filter() method to select elements from Stream based upon some conditions.

3) Use flatMap() method to both flatten the Stream as well as transform it like map method.

4) The toArray() method is used to convert Stream to array in Java. 

5) The distinct method can be used to remove duplicate elements from Stream. 

6) The sorted method of Stream can be used to sort elements of Stream in their natural order or a custom order defined by Comparator. 

7) The peek method can be used for debugging a Stream pipeline. It can print messages after each operation on Stream.

8) The static factory method Stream.of() can be used to create Stream from given objects. 

9) The collect() method of Stream class can be used to collect the elements of Stream into different type of Collection objects like List, Set or Map with the help of Collectors. 

10 The findFirst() method can be used to retrieve the first element from Stream. 

That's all about examples of essential methods of Stream class in Java.  Once you have goe through these examples, you would learn how to use Stream API and when to use these methods. Even though Stream class is big and got a lot more methods, these are those 20% of methods which you will use 80% of time. 

I have explained all these methods with an example of List of integers and as an exercise you can try out all these method by using List of String. That will not only help you to better understand their usage but also develop the intuition and coding sense you need while writing code in Java 8. If you need any help you can always refer following resources for reference:

Other Java 8 and Stream articles you may like:
  • Top 5 Courses to Learn Java 8 Programming (courses)
  • 16 Stream and Lambda Interview questions (stream questions)
  • Java 8 map + filter + stream example (tutorial)
  • 15 Java Stream and Functional Programming questions (functional questions)
  • How to join String in Java 8 (example)
  • 5 Books to Learn Java 8 from Scratch (books)
  • How to use Stream class in Java 8 (tutorial)
  • How to compare two Dates in Java 8? (example)
  • 10 Java Date, Time, and Calendar based Questions from Interviews (questions)
  • How to format/parse the date with LocalDateTime in Java 8? (tutorial)
  • 10 Examples to format and parse Date in Java 8? (tutorial)
  • How to use forEach() method in Java 8 (example)
  • How to change the date format of String in Java 8? (tutorial)
  • How to convert Timestamp to Date in Java? (example)
  • 20 Examples to learn new Date and Time API in Java 8 (example)
  • 5 Free Courses to learn Java 8 and 9 (courses)
  • 50 Java 8 Stream and Lambda Interview questions (java 8 questions)

Thanks for reading this article so far. If you like these Java 8 Stream examples and if this tutorial helped you to learn Stream API better than please share with your friends in Facebook and Twitter. If you have any questions or feedback then please drop a note.

P. S. - If you want to learn Stream class and API in depth and looking for online courses then you can also checkout these Stream and Collection online courses where I have shared best online courses to learn Stream API in Java. 

No comments:

Post a Comment