Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How intermediate and terminal stream methods works in this case

I have this code snippet in Java:

List<Integer> li= Arrays.asList(1, 2, 3, null, 4, 5, null, 6, 7, 8);
li.stream().filter(e-> e!=null)
           .map(e-> {System.out.print(" m "+ e); return e * 2;})
           .filter(e-> e % 3 == 0)
           .forEach(e-> {System.out.println(" fe " + e);});

The output is

 m 1 m 2 m 3 fe 6
 m 4 m 5 m 6 fe 12
 m 7 m 8

I know filter, map are intermediate methods, which means they only start to work, when a terminal method like forEach is called. But I can't just wrap my mind around the exact order in which they are called and start to work in this case. Can someone explain to me how this output comes to be?

like image 973
Huy Tran Avatar asked Jan 29 '23 19:01

Huy Tran


2 Answers

Let the code itself tell its story.

Here is your code, with additional printouts:

import java.util.*;
import java.util.stream.*;

public class StreamEvaluationOrder {
    private static void p(String s) {
        System.out.println(s);
    }
    public static void main(String[] args) {
        List<Integer> li= Arrays.asList(1, 2, 3, null, 4, 5, null, 6, 7, 8);

        li.stream()
            .map(x -> { p("The element " + x + " is pulled from the list"); return x; })
            .filter(e-> { 
                boolean res = e != null; 
                if (res) p(e + " is not null");
                else p(e + " is null, therefore filtered out.\n"); 
                return res;
            })
            .map(e-> { 
                int res = e * 2; 
                p(e + " is mapped to " + res); 
                return res;
            })
            .filter(e-> {
                boolean res = e % 3 == 0;
                if (res) p(e + " is divisible by three");
                else p(e + " is not divisible by three, therefore filtered out. \n");
                return res;
            })
            .forEach(e-> {
                p(e + " reaches the forEach.\n");
            });
    }
}

Here is the story that it tells you:

The element 1 is pulled from the list
1 is not null
1 is mapped to 2
2 is not divisible by three, therefore filtered out. 

The element 2 is pulled from the list
2 is not null
2 is mapped to 4
4 is not divisible by three, therefore filtered out. 

The element 3 is pulled from the list
3 is not null
3 is mapped to 6
6 is divisible by three
6 reaches the forEach.

The element null is pulled from the list
null is null, therefore filtered out.

The element 4 is pulled from the list
4 is not null
4 is mapped to 8
8 is not divisible by three, therefore filtered out. 

The element 5 is pulled from the list
5 is not null
5 is mapped to 10
10 is not divisible by three, therefore filtered out. 

The element null is pulled from the list
null is null, therefore filtered out.

The element 6 is pulled from the list
6 is not null
6 is mapped to 12
12 is divisible by three
12 reaches the forEach.

The element 7 is pulled from the list
7 is not null
7 is mapped to 14
14 is not divisible by three, therefore filtered out. 

The element 8 is pulled from the list
8 is not null
8 is mapped to 16
16 is not divisible by three, therefore filtered out. 

The principle is that it pulls the elements from the source stream one by one, pushes each element through the entire pipeline, and only then begins to process the next element. Is it clearer now?

like image 76
Andrey Tyukin Avatar answered Jan 31 '23 09:01

Andrey Tyukin


  • m 1: 1 passes first filter, then is transformed to 2 after print and then doesn't pass second filter.
  • m 2: all the same.
  • m3 fe 6: 3 passes first filter, then is transformed to 6, then passes second filter.
  • m 4, m 5: same as for 1.
  • m 6 fe 12: same as for 3.
  • m 7, m 8: same as for 1.

The thing is that each element in stream goes through whole pipeline (or at least until it is not filtered our) and only after that the next element is picked up.

like image 30
Ivan Avatar answered Jan 31 '23 08:01

Ivan