mah454
mah454

Reputation: 1919

Java 8 stream computation on whole list

I see Let’s Get Lazy: Explore the Real Power of Streams video on youtube (venkat subramaniam). (minute 26-30 approx.)

In the example, a for loop:

List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 4, 6, 7, 8, 9, 10);
int result = 0;
for(int e: values){
  if(e > 3 && e % 2 == 0){
    result = e * 2;
    break;
  }
}  

having 8 "unit operations"

according to his example :

public class MainClass {
    public static void main(String[] args) {

        List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 4, 6, 7, 8, 9, 10);

        System.out.println(
                numbers.stream()
                        .filter(e -> e > 3)
                        .filter(e -> e % 2 == 0)
                        .map(e -> e * 2)
                        .findFirst()
                        .orElse(0)
        );


    }
}

this code looks like it has 21 "unit operations".

And then he recommends to use this code:

public class MainClass {
    public static void main(String[] args) {

        List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 4, 6, 7, 8, 9, 10);

        System.out.println(
                numbers.stream()
                        .filter(MainClass::isGT3)
                        .filter(MainClass::isEven)
                        .map(MainClass::doubleIt)
                        .findFirst()
                        .orElse(0)
        );


    }

    private static int doubleIt(Integer e) {
        return e * 2;
    }

    private static boolean isEven(Integer e) {
        return e % 2 == 0;
    }

    private static boolean isGT3(Integer e) {
        return e > 3;
    }
}

Really I want to understand, how can this be proved that has 8 unit operations instead of 21 unit operations?

Upvotes: 3

Views: 550

Answers (1)

developer_hatch
developer_hatch

Reputation: 16224

No no no, you misunderstood the idea. The idea is the lazy evaluation on streams. And both takes "8 computations" (using his words), he tried to say that it looked like it would take 21.

This

  numbers.stream()
                  .filter(MainClass::isGT3)
                  .filter(MainClass::isEven)
                  .map(MainClass::doubleIt)
                  .findFirst()
                  .orElse(0)

And

numbers.stream()
                .filter(e -> e > 3)
                .filter(e -> e % 2 == 0)
                .map(e -> e * 2)
                .findFirst()
                .orElse(0)

Are Exactly the same. The only difference is create a function to be more readable and that's all. The imperative code:

List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 4, 6, 7, 8, 9, 10);
int result = 0;
for(int e: values){
  if(e > 3 && e % 2 == 0){
    result = e * 2;
    break;
  }
}

and the code in the streams, computes exact the same, because they are called on demand, it means it doesn't filter all > 3 and the filter all % 2 == 0, no, it combines that operations, and then apply it when a terminal function is called (like findFirst())

As the video shows, if you put some prints between the functions, it will show the 8 operations:

public class Main {

    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 5, 4, 6, 7, 8, 9, 10);

        System.out.println(processStream(numbers)); 
        System.out.println(getNumber(numbers));
        System.out.println(getNumberEfficient(numbers));
    }

    static Stream<Integer> processStream(List<Integer> numbers){
        return numbers.stream()
                .filter(e -> {
                    System.out.println("GT3: " + e);
                    return e > 3;
                })
                .filter(e -> {
                    System.out.println("is Even: " + e);
                    return e % 2 == 0;
                })
                .map(e -> {
                    System.out.println("times 2: " + e);
                    return e * 2;
                } );
    }

    static int getNumberEfficient(List<Integer> numbers){
        return numbers.stream()
                .filter(e -> {
                    System.out.println("GT3 and even: " + e);
                    return e > 3 && e % 2 == 0;
                })
                .map(e -> {
                    System.out.println("times 2: " + e);
                    return e * 2;
                } )
                .findFirst()
                .orElse(0);
    }

    static int getNumber(List<Integer> numbers){
        return numbers.stream()
                .filter(e -> {
                    System.out.println("GT3: " + e);
                    return e > 3;
                })
                .filter(e -> {
                    System.out.println("is Even: " + e);
                    return e % 2 == 0;
                })
                .map(e -> {
                    System.out.println("times 2: " + e);
                    return e * 2;
                } )
                .findFirst()
                .orElse(0);
    }
}

it will return:

This is the pipeline, nothing was executed, because is laze

java.util.stream.ReferencePipeline$3@7ba4f24f

Thees are the 8 operations:

GT3: 1
GT3: 2
GT3: 3
GT3: 5
is Even: 5
GT3: 4
is Even: 4
times 2: 4
8

And this is a little optimization, instead of doing two filters, you can do only one, and reduce from 8 to 6:

GT3 and even: 1
GT3 and even: 2
GT3 and even: 3
GT3 and even: 5
GT3 and even: 4
times 2: 4
8

Upvotes: 4

Related Questions