4

As simple as:

import java.util.stream.*; public class MyClass { public static void main(String args[]) { Long x = Stream.of(1, 2, 3).map(i -> { System.out.println(i); return i + 4; }).count(); System.out.println(x); // prints '3' } } 

The count() here is used in order to trigger the intermediate operations which include System.out.println(i), but nothing gets printed from inside map(). Why is that?

7
  • 5
    Can you find the documentation that says the map function needs to be called? This is why you don't use side effects in your streams. Commented May 16, 2023 at 12:30
  • I get "1, 2, 3, 3" as output. Commented May 16, 2023 at 12:31
  • 3
    @Mr.Polywhirl Probably depends on the compiler / JIT you're running it on. This is due to an optimization. Commented May 16, 2023 at 12:33
  • 3
    @MaartenBodewes it’s a well known optimization of the Stream API implementation introduced in Java 9 and the canonical example of why programmers should make no assumptions about the evaluation of intermediate operations. Since it was expected that developers get surprised, the documentation even mentions this. No JIT magic involved. Commented May 16, 2023 at 14:52
  • 2
    @MaartenBodewes imperative code continues to work as it used to do. The Stream API is specifically for specifying an operation in a functional/declarative way and accepting that there is less control over the evaluation. The problem here is not that the implementation skips over a method with side effects but that this function has a side effect to begin with. Loops still exists and are neither deprecated nor discouraged; they are still the preferred approach for side effects. Commented May 17, 2023 at 10:18

3 Answers 3

10

The javadoc for count() has note that:

An implementation may choose to not execute the stream pipeline (either sequentially or in parallel) if it is capable of computing the count directly from the stream source. In such cases no source elements will be traversed and no intermediate operations will be evaluated.

Sign up to request clarification or add additional context in comments.

2 Comments

That's an update from jdk8
@matt this update was introduced with Java 9
9

Streams are lazy and smart. Since your terminal operation is count, which does not care of individual elements, but only of their number, map isn't called.
Replace count with something that would need to calculate individual elements, like sum - and map will be called.

Long x = Stream.of(1, 2, 3).map(i -> { System.out.println(i); return i + 4; }) .mapToLong(Long::valueOf) .sum(); // prints 1 2 3 System.out.println(x); // prints 18 

Also a person in comments rightfully reminds you side effects.

1 Comment

.filter($->true)
1

Directly calling count actually not helps to run the map. but if we try to call the toList and Size for the count. seems working for me.

Integer x = Stream.of(1, 2, 3).map(i -> { System.out.println(i); return i + 4; }) .toList().size(); 

1 Comment

I guess that this requires a clear DEBUG comment though, because the next programmer may simply use count() again, or the compiler may get "smarter" and still skip the println statement. That said, if you perform println in a function a "TODO fix" comment should already be there.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.