Java 8 Stream Examples and Tutorial

It's been a couple of weeks Java 8 has released and a lot of Java developers are trying their hands on major enhancement e.g. lambda expression, method reference, new data and time classes, and more importantly Stream API for bulk data operations. In my opinion, the best way to learn any new feature or functionality is by writing short examples, and this is what prompt me to write this article. In this Java 8 tutorial, I have shared simple examples of java.util.Stream package. Streams are one of the most important addition on JDK, it allows you to leverage other changes e.g. lambda expression, method reference, functional interface and internal iteration introduced via forEach() method. Some of the most common things we do with Streams are filtering a collection, applying map and reduce function on all elements of the collection and taking advantage of lazy evaluation, built-in parallelism via parallelStream().

This is by no means a complete set of examples you need to master Java 8 Stream API, but it will introduce with key functions and encourage you to explore by yourself by reading Java documentation and trying them. If you like to also learn lambda expression in details, you can check my post 10 ways to use lambda expression in Java 8.


How to use Streams in Java 8

You can use Streams to do lot of things in Java 8. By the way this stream is bit different than your Java IO streams e.g. InputStream and OutputStream. This stream provides elegant lazy evaluation of expression, and it also support intermediate and terminal operations. Terminal operations are used to produce result and after that you cannot reuse them.


Good thing about Streams is that they leave source collection intact i.e. operations on streams doesn't affect the collection from which streams are obtained. By the way, you can get Stream not just from Collection but from other sources e.g. Random Number generator and FileInputStream. In fact, stream API is a handy abstraction for working with aggregated data, particularly when we need to perform multiple actions, such as transforming the content, apply some filters and perhaps group them by a property.

Since collection is going to be starting point for stream, I have used List for all my examples. Once you know basics, you can also apply it to other Collection classes e.g. HashSet or HashMap. Now let's see the code and then we will talk about each example.

Java 8 Stream API Example Filter Map Reduceimport java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.stream.Collectors;

/**
  * Java program to demonstrate how to use Java 8 Stream API with simple
  * examples like filter objects, transforming objects and creating subsets.
  * @author http://java67.blogspot.com
  */
public class Java8Streams{

    public static void main(String args[]) {

        // Count the empty strings
        List<String> strList = Arrays.asList("abc", "", "bcd", "", "defg", "jk");
        long count = strList.stream().filter(x -> x.isEmpty()).count();
        System.out.printf("List %s has %d empty strings %n", strList, count);

        // Count String with length more than 3
        long num = strList.stream().filter(x -> x.length()> 3).count();
        System.out.printf("List %s has %d strings of length more than 3 %n", strList, num);
     
     
        // Count number of String which startswith "a"
        count = strList.stream().filter(x -> x.startsWith("a")).count();
        System.out.printf("List %s has %d strings which startsWith 'a' %n", strList, count);
     
        // Remove all empty Strings from List
        List<String> filtered = strList.stream().filter(x -> !x.isEmpty()).collect(Collectors.toList());
        System.out.printf("Original List : %s, List without Empty Strings : %s %n", strList, filtered);
     
        // Create a List with String more than 2 characters
        filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList());
        System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);
     
     
        // Convert String to Uppercase and join them using coma
        List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
        String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
        System.out.println(G7Countries);
     
        // Create List of square of all distinct numbers
        List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
        List<Integer> distinct = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
        System.out.printf("Original List : %s,  Square Without duplicates : %s %n", numbers, distinct);
     
        //Get count, min, max, sum, and average for numbers
        List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
        IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
        System.out.println("Highest prime number in List : " + stats.getMax());
        System.out.println("Lowest prime number in List : " + stats.getMin());
        System.out.println("Sum of all prime numbers : " + stats.getSum());
        System.out.println("Average of all prime numbers : " + stats.getAverage());
    }

}

Output:
List [abc, , bcd, , defg, jk] has 2 empty strings
List [abc, , bcd, , defg, jk] has 1 strings of length more than 3
List [abc, , bcd, , defg, jk] has 1 strings which startsWith 'a'
Original List : [abc, , bcd, , defg, jk], List without Empty Strings : [abc, bcd, defg, jk]
Original List : [abc, , bcd, , defg, jk], filtered list : [abc, bcd, defg]
USA, JAPAN, FRANCE, GERMANY, ITALY, U.K., CANADA
Original List : [9, 10, 3, 4, 7, 3, 4],  Square Without duplicates : [81, 100, 9, 16, 49]
Highest prime number in List : 29
Lowest prime number in List : 2
Sum of all prime numbers : 129
Average of all prime numbers : 12.9


Java 8 Filter Example : Counting Empty String

List<String> strList = Arrays.asList("abc", "", "bcd", "", "defg", "jk");
long count = strList.stream().filter(x -> x.isEmpty()).count();

This is a good example is to demonstrate how you can filter certain object from Collection and create subset of elements which satisfy given criterion. In second line strList.stream() returns a Stream and then we use filter() method, which accept a Predicate. Since java.util.function.Predicate is functional interface ( an interface with just one abstract method), we can pass lambda expression instead of an instance of Predicate interface. Here we can define code to specify condition. This code will go to the test() method of Predicate, and will be applied to each element during internal iteration. All elements which are empty are counted by count() method, which is a terminal operation. After this line you can not call any method on this Stream. Remember filter() is a tricky method, it does not filter element from original collection, instead it selects element which satisfy criterion and return them in new Collection. By the way, you are not bounded to isEmpty() method, you can use any of these ways to check if String is empty in Java.



Java 8 Filter Example 2 : Count String whose length is more than three

long num = strList.stream().filter(x -> x.length()> 3).count();

This is similar to previous example of Stream with just one difference, instead of isEmpty() method we are using length() method of String. Remember, Java 8 is doing some really advanced type inference, that's why once you specify type parameter in List, no need to declare it again, Java 8 will infer it from there. This is the reason you can call all method of java.lang.String on variable x, whose type was not declared inside lambda expression.



Java 8 Filter Example 3 : Count number of String which starts with "a"

long count = strList.stream().filter(x -> x.startsWith("a")).count();

This is also exactly similar to previous two examples, by doing all three examples, you should feel more comfortable with filter() method. This is now the standard technique to filter elements in Java Collection. You can specify arbitrary any condition on lambda expression to declare filtering logic. For example, in this code, we are creating a subset of String which are starting with "a" and then counting them by using count() method.


Java 8 Collectors Example : Remove all empty Strings from List

List<String> filtered = strList.stream().filter(x -> !x.isEmpty()).collect(Collectors.toList());

Now, this example is little bit different than previous three. Here we are again using filter() method to create subset of all string which are non-empty but instead of counting, we are now calling static utility method Collectors.toList() to return them as List. Collectors class are similar to Collections, full of static methods, which you can use along with Collection. You can wrap filtered elements into a Set or List by using Collectors class.


Java 8 Collectors Example 2 : Create a List with String more than 2 characters

List<String> filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList());

In this example, ware again using filter() method and Collectors class, but our filtering criterion is different. After doing this example, you should be comfortable with creating subset from original collection.


Java 8 Map functional Example : Convert String to uppercase and Join them with coma

List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", ")); 

So far we have seen examples of only filter() method, in this example we will learn how to use map() function. This is similar to Map concept of functional programming paradigm, like here we are converting each String to upper case and then finally we have joined all String using Collectors.joining(","), another utility method which can join String by using given delimiter.



Java 8 Map Example 2 : Create List of square of all distinct numbers

List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
List<Integer> distinct = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());

This is another example of using map() method, here we are mapping each element to their square and then filtering out all duplicate elements by calling distinct() method. Finally by using collect() method we are gathering output into a List.


Java 8 Statistics Example :  Get count, min, max, sum, and average for numbers

List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();

This our final example of Stream API, in this example we will learn how to get some statistics data from Collection e.g. finding minimum or maximum number from List, calculating sum of all numbers from a numeric list or calculating average of all numbers form List. Since this statistics operations are numeric in nature, its important to call mapToInt() method. After this we call summaryStatistics(), which returns an instance of IntSummaryStatistics. It is this object which provides us utility method like getMin(), getMax(), getSum() or getAverage(). By using these general purpose methods, you can easily do lot of things which require lot of code before Java 8.


That's all about how to use Stream API in Java 8. I have barely scratched surface with these examples, streams have several gems to offer. At very minimum, every Java developer now should know about filtering elements, and applying map function to transform them. For further reading, you can start exploring java.util.stream package and java.util.function package. These two packages has lot of interesting things to offer.


6 comments:

  1. nice and to the point
    thanks java67

    ReplyDelete
  2. Great tutorial love examples. Thanks once again

    ReplyDelete
  3. Hi javin,
    Please, I am just curious with a strange behavior with lambda expressions in Java 8. Using it with a PriorityQueue, List is as simple as doing this :
    list.forEach(value->System.out.println(value));

    BUT WITH AN ARRAY, THE SYNTAX IS A BIT COMPLEX:

    Arrays.Stream(array).forEach(value->System.out.println(value));

    Why do I have to wrap my array in a Stream before using it while with Lists, I just use them straight away?

    ReplyDelete
    Replies
    1. forEach() method is defined in Stream class and since there is no method in Java array, a method is added on Arrays class to convert array to Stream. On the other hand, List gets its forEach() method from Iterable interface, which is modified to add this method in Java 8.

      Delete