What are Java 8 Streams?

0

In this article, we will understand what are streams and why we need them?

The best way to understand streams is to review an example that involves processing a list. For example, picking up the top 3 action movies from a list of movies provided to you.

The usual way before the introduction of streams was to iterate over the list, do some work on each element to figure out whether the element fits the criteria and then continue with the next element, if available, till we reach end of the list. We will know it when iterator.hasNext() returns us false.

Let’s write the code.

StreamIteratorComparison:

package com.javarticles.java8.stream;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class StreamIteratorComparison {

    public static void main(String[] args) {
        List<Movie> movies = Movie.getMovieList();
        System.out.println("-Find top 3 action movies using iterator-");
        List<Movie> actionMovies = new ArrayList<Movie>();
        for (Movie movie : movies) {
            if (movie.getGenre() != Genre.ACTION) {
                continue;
            }
            actionMovies.add(movie);
        }
        Collections.sort(actionMovies, new Comparator<Movie>() {

            @Override
            public int compare(Movie m1, Movie m2) {
                return Double.compare(m2.getRating(), m1.getRating());
            }
        });
        for (int i = 0; i < 3; i++) {
            System.out.println(actionMovies.get(i).getName());
        }
}

Output:

-Find top 3 action movies using iterator-
Star Wars
Die Hard
The Avengers

Now Using streams…

StreamIteratorComparsion:

package com.javarticles.java8.stream;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class StreamIteratorComparison {

    public static void main(String[] args) {
        List<Movie> movies = Movie.getMovieList();
        System.out.println("-Find top 3 action movies using iterator-");
        List<Movie> actionMovies = new ArrayList<Movie>();
        for (Movie movie : movies) {
            if (movie.getGenre() != Genre.ACTION) {
                continue;
            }
            actionMovies.add(movie);
        }
        Collections.sort(actionMovies, new Comparator<Movie>() {

            @Override
            public int compare(Movie m1, Movie m2) {
                return Double.compare(m2.getRating(), m1.getRating());
            }
        });
        for (int i = 0; i < 3; i++) {
            System.out.println(actionMovies.get(i).getName());
        }

        System.out.println("-Top 3 action movies using stream-");
        movies.stream()
              .filter(movie -> movie.getGenre() == Genre.ACTION)
              .sorted(Comparator.comparing(Movie::getRating).reversed())
              .map(Movie::getName)
              .limit(3)
              .forEach(System.out::println);
    }
}

Output:

-Find top 3 action movies using iterator-
Star Wars
Die Hard
The Avengers
-Top 3 action movies using stream-
Star Wars
Die Hard
The Avengers

What was our requirement?
We were provided with a list of movies, we needed to filter them by ‘ACTION’ genre, sort the list by its rating, get the top 3 movies, their names and print each one of them.
Let’s analyse the stream API used.

        movies.stream()
              .filter(movie -> movie.getGenre() == Genre.ACTION)
              .sorted(Comparator.comparing(Movie::getRating).reversed())              
              .limit(3)
              .map(Movie::getName)
              .forEach(System.out::println);
Example of Stream

Example of Stream

If you compare both the approaches, we can say:

  1. Stream API looks explicit. One can easily understand the intent of the author
  2. There is a clear elimination of boilerplate code
  3. Iteration of the collection is pushed to the stream framework
  4. We just need to focus on what is required from the list rather than the ‘how’ part

External and Internal Iteration

Iteration and application logic that work on the individual elements are two different concerns. Streams aim at decoupling both these concerns.
In the below diagram, we show that in the pre-stream world, the iteration was handled in the application code by the user using for-each or Iterator. The logic of traversing the elements is intermingled with the functionality that is applied to each element in the sequence and the actual intent of the application logic that works on these iterated elements gets diluted in the boilerplate code around it.
Separating the concerns would allow for optimizations regarding how the elements are accessed.

Iteration and Application Code

Iteration and Application Code

With the introduction of streams, the ‘What’ and ‘How’ are decoupled. ‘What’ represents the criteria that we want to build and ‘How’ represents the execution of the criteria which is now handled completely by the framework. Stream API aims at further decoupling the ‘What’ part by structuring the API into two parts. This eases the way user builds the criteria and actual execution of criteria is only handled in the end.

  1. stream – call stream on the data source like list, array, map etc.
  2. Intermediate Operations – these operations don’t perform any processing, they help building the criteria, until a terminal operation is invoked on the stream pipeline
  3. Terminal operation – this is the final operation that triggers the iteration and processes the stream pipeline to return a result.

The last steps control the details of the iteration process. This is called internal iteration.

Stream Components

Stream Components

Stream Examples

Before I conclude this article, I will leave you with some more examples on stream.

Here is the Movie bean. It also contains method getMovieList() to return a sample of movies on which we will run our examples.

Movie:

package com.javarticles.java8.stream;

import java.util.Arrays;
import java.util.List;

public class Movie {
    private String name;
    private Genre genre;
    private double rating;

    Movie(String name, Genre genre, double rating) {
        this.name = name;
        this.genre = genre;
        this.rating = rating;
    }

    public String toString() {
        return name + "(" + genre + ", " + rating + ")";
    }

    public String getName() {
        return name;
    }

    public Genre getGenre() {
        return genre;
    }

    public double getRating() {
        return rating;
    }
    
    public static List<Movie> getMovieList(){
        return Arrays.asList(movies);
    }
        
    private static final Movie[] movies = new Movie[] { m("Green Mile", Genre.DRAMA, 8.5),
            m("Forrest Gump", Genre.DRAMA, 8.8),
            m("A Beautiful Mind", Genre.DRAMA, 8.2),
            m("The Notebook", Genre.ROMANTIC, 7.9),
            m("The Titanic", Genre.ROMANTIC, 7.7),
            m("Pretty Woman", Genre.ROMANTIC, 6.9),
            m("Notting Hill", Genre.ROMANTIC, 7),
            m("Inception", Genre.THRILLER, 8.8),
            m("The Game", Genre.THRILLER, 7.8),
            m("Seven", Genre.THRILLER, 8.7),
            m("The Dark Knight", Genre.THRILLER, 9),
            m("The Exorcist", Genre.HORROR, 8),
            m("The Shinning", Genre.HORROR, 8.5),
            m("The Cabin in the Woods", Genre.HORROR, 7),
            m("Insidious", Genre.HORROR, 6.8),
            m("The Avengers", Genre.ACTION, 8.2),
            m("Die Hard", Genre.ACTION, 8.3),
            m("Casino Royale", Genre.ACTION, 8),
            m("Star Wars", Genre.ACTION, 8.7), m("Thor", Genre.ACTION, 7) };
    
    private static Movie m(String name, Genre genre, double rating) {
        return new Movie(name, genre, rating);
    }
}

StreamExamples:

package com.javarticles.java8.stream;

import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

public class StreamExamples {

    public static void main(String[] args) {
        List<Movie> movies = Movie.getMovieList();       
        System.out.println("-Find the average of all the movie ratings-");
        System.out.println(
                movies
                .stream()
                .mapToDouble(Movie::getRating)
                .average()
                .getAsDouble());
       
        System.out.println("-Filter movies with rating > 8.5-");
        movies
        .stream()
        .filter(movie -> movie.getRating() > 8.5)
        .forEach(System.out::println);
        
        System.out.println("-What is the lowest rating-");
        System.out.println(
                movies
                .stream()
                .mapToDouble(Movie::getRating)
                .min()
                .getAsDouble());
        
        
        System.out.println("-Find movie with the lowest rating-");
        System.out.println(
                movies
                .stream()
                .min(Comparator.comparing(Movie::getRating))
                .get());        
        
        System.out.println("-Count of movies with rating > 8.5-");
        System.out.println(
                movies
                .stream()
                .mapToDouble(Movie::getRating)
                .filter(rating -> rating > 8.5)
                .count());
        
        System.out.println("-Get the summary statistics based on rating-");
        System.out.println(
                movies
                .stream()
                .mapToDouble(Movie::getRating)
                .summaryStatistics());
        
        System.out.println("-List of horror movies, comma separated-");
        System.out.println(
                movies
                .stream()
                .filter(movie -> movie.getGenre() == Genre.HORROR)
                .map(Movie::getName)
                .collect(Collectors.joining(", ")));
        
        System.out.println("-List of action movies, print using foreach-");
        movies
        .stream()
        .filter(movie -> movie.getGenre() == Genre.ACTION)
        .map(Movie::getName)
        .forEach(System.out::println);
        
        System.out.println("-List of action movies, sorted, print using foreach-");
        movies
        .stream()
        .filter(movie -> movie.getGenre() == Genre.ACTION)
        .map(Movie::getName)
        .sorted()
        .forEach(System.out::println);
                
        System.out.println(
                "-List of action movies, sorted, mapped to upper case, print using foreach-");        
        movies
        .stream()
        .filter(movie -> movie.getGenre() == Genre.ACTION)
        .map(Movie::getName)
        .map(String::toUpperCase)
        .sorted()
        .forEach(System.out::println);
        
        System.out.println("-Top 3 action movies-"); 
        movies
        .stream()
        .filter(movie -> movie.getGenre() == Genre.ACTION)
        .sorted(Comparator.comparing(Movie::getRating).reversed())
        .map(Movie::getName)
        .limit(3)
        .forEach(System.out::println);

        System.out.println("-Find the top rated movie-");
        System.out.println(
                movies
                .stream()
                .sorted(Comparator.comparing(Movie::getRating).reversed())
                .findFirst()
                .get());
        
        System.out.println("-Does the movie list have rating >= 9.0?-"
                + movies
                .stream()
                .anyMatch(movie -> movie.getRating() >= 9.0));
    }
}

Output:

-Find the average of all the movie ratings-
7.99
-Filter movies with rating > 8.5-
Forrest Gump(DRAMA, 8.8)
Inception(THRILLER, 8.8)
Seven(THRILLER, 8.7)
The Dark Knight(THRILLER, 9.0)
Star Wars(ACTION, 8.7)
-What is the lowest rating-
6.8
-Find movie with the lowest rating-
Insidious(HORROR, 6.8)
-Count of movies with rating > 8.5-
5
-Get the summary statistics based on rating-
DoubleSummaryStatistics{count=20, sum=159.800000, min=6.800000, average=7.990000, max=9.000000}
-List of horror movies, comma separated-
The Exorcist, The Shinning, The Cabin in the Woods, Insidious
-List of action movies, print using foreach-
The Avengers
Die Hard
Casino Royale
Star Wars
Thor
-List of action movies, sorted, print using foreach-
Casino Royale
Die Hard
Star Wars
The Avengers
Thor
-List of action movies, sorted, mapped to upper case, print using foreach-
CASINO ROYALE
DIE HARD
STAR WARS
THE AVENGERS
THOR
-Top 3 action movies-
Star Wars
Die Hard
The Avengers
-Find the top rated movie-
The Dark Knight(THRILLER, 9.0)
-Does the movie list have rating >= 9.0?-true

Download the source code

This was an article on Java 8 Streams. You can download the source code here: streamExamples.zip

Share.

Comments are closed.