Site iconJava PDF Blog

Java 8 streams Explained in 5 minutes

In my previous article I explained Java 8 Lambda Expression and how easy it is to use. In this article, we will take a quick look at Java 8 Streams .

Stream is a new feature in Java 8. They are used to create pipelines of operations. It is divided into two operations, which are the terminal, and intermediate operation. Lets take a look at the example below.

 private static void printNames(List persons, Predicate predicate) {
            persons.stream()
                    .filter(predicate)
                    .map(p -> ((Person) p).getName())
                    .forEach(name -> System.out.println(name));
        }
       
    }

In the code above, the filter(predicate) , map(p -> ((Person) p).getName()) and the sorted() are the intermediate operations while the forEach(name -> System.out.println(name)) is the terminal operations.

Intermediate operations are lazy.What this means is they do not actually perform any work until the terminal operation is being called.Intermediate operation such as filter produces a new stream from the main stream that matches the predicate being given to it.

Intermediate operations are also divided into two operations which are the stateful and stateless opeartion. The stateless operations such as the filter() and map() does not keep state of previous data seen when processing new data whiles the stateful operation i.e sorted() may include states from previous seen data when producing new data.

Now lets take a look at the code below and see how Java 8 Streams are used.

    import java.util.ArrayList;
    import java.util.List;
    import java.util.function.Predicate;
     
    public class Lambda {
       
        private enum Gender  { MALE, FEMALE }
       
        private static class Person {
           
            private final String name;
            private final int age;
            private final Gender gender;
           
            public Person(String name, int age, Gender gender) {
                this.name = name;
                this.age = age;
                this.gender = gender;
            }
           
            public String getName() {
                return name;
            }
           
            public int getAge() {
                return age;
            }
           
            public Gender getGender() {
                return gender;
            }
        }
       
       
        public static void main(String[] args) {
           
            List persons = new ArrayList<>();
            persons.add(new Person("Albert", 80, Gender.MALE));
            persons.add(new Person("Ben", 15, Gender.MALE));
            persons.add(new Person("Charlote", 20, Gender.FEMALE));
            persons.add(new Person("Dean", 6, Gender.MALE));
            persons.add(new Person("Elaine", 17, Gender.FEMALE));
           
            // How much code would you need to do the following without Lambdas?
            System.out.println("----------Printing Persons with age less than 18----------");
            printNames(persons, p -> ((Person) p).getAge() < 18);
            System.out.println("\n--------Printing all Males-------------");
            printNames(persons, p -> ((Person) p).getGender() == Gender.MALE);
            System.out.println("\n---------Printing Persons with Names starting With C------------");
            printNames(persons, p -> ((Person) p).getName().startsWith("C"));
           
        }
       
  private static void printNames(List persons, Predicate predicate) {
            persons.stream()
                    .filter(predicate)
                    .map(p -> ((Person) p).getName())
                    .forEach(name -> System.out.println(name));
        }
       
    }

If you found this article useful, you may also be interested in our other Java8 posts on Lambda Expression, Consumer Suppliers, Default Methods / Method References, Optional and Repeating Annotations.

We also now have large collection of articles on what is new in other versions of Java, including Java9