1
public static void main(String[] args) throws Exception {
    List<String> list = new ArrayList<>();
    list.add("1");
    list.add("2");
    list.add("3");
    list.add("4");

    // below code does not work
    list.stream().filter(new Predicate<String>() {
        @Override
        public boolean test(String s) {
            System.out.println("1:test:" + s);
            return true;
        }
    }).map(s -> {
        System.out.println("1:map:" + s);
        return s;
    });
    
    // below code works
    list.stream().filter(new Predicate<String>() {
        @Override
        public boolean test(String s) {
            System.out.println("2:test" + s);
            return true;
        }
    }).map(s -> {
        System.out.println("2:map" + s);
        return s;
    }).collect(Collectors.toList());
}

The output of the above code is:

2:test1
2:map1
2:test2
2:map2
2:test3
2:map3
2:test4
2:map4

I don't know why it didn't work the first time.

Gautham M
  • 4,816
  • 3
  • 15
  • 37
U2647
  • 450
  • 4
  • 10
  • 1
    Streams are lazily evaluated. – Eran Jun 23 '21 at 09:38
  • The first stream will not be evaluated at all, because it doesn't have a terminal operation. – daniu Jun 23 '21 at 09:40
  • 2
    Why do you use a verbose anonymous inner class creation for the predicates when you know about lambda expressions, as shown with the map functions? – Holger Jun 23 '21 at 10:47

2 Answers2

1

The doSomething in the lambda p -> doSomething(p) would be called only when the lambda is executed. The simple presence of that statement does not guarantee execution, whereas a normal (not a lambda) statement doSomething(x) guarantees execution.

map and filter are intermediate operations, whereas collect is a terminal operation. For each intermediate operation, a new stream object (any implementation of Stream like StatelessOp, StatefulOp or primitive specialization of streams like IntStream, LongStream etc.) is returned, which stores any information like the mapping or filtering function as in your case. Actual execution does not happen here.

Stream evaluation happens with the invocation of evaluate method. Now, if you see any terminal operation, it would invoke this evaluate method. This is were the lambdas you pass as argument to the intermediate operations are applied to the stream elements. That is why streams are said to be lazily evaluated -> Evaluation only starts with application of a terminal operation.

Hence you first code block does not work, because there are no terminal operations, and the lambdas you passed are not executed. But in the second code block you call a terminal operation and stream is evaluated. Also note that the intermediate operation need not be executed for every stream element -> Refer

You could see the implementation of each stream method in ReferencePipeline.

Gautham M
  • 4,816
  • 3
  • 15
  • 37
0

A stream pipeline consists of a source (which might be an array, a collection, a generator function, an I/O channel, etc), zero or more intermediate operations (which transform a stream into another stream, such as filter(Predicate)), and a terminal operation (which produces a result or side-effect, such as count() or forEach(Consumer)). Streams are lazy; computation on the source data is only performed when the terminal operation is initiated, and source elements are consumed only as needed.

Read more here

DDovzhenko
  • 1,295
  • 1
  • 15
  • 34