0
import java.util.stream.*;

public class MyClass {
    public static void main(String args[]) {
      Stream.of(1, 2, 3).map(i -> {
          System.out.println(i+":inside map");
          return i+4;
       }).forEach(t->System.out.println(t + ":inside foreach"));
    }
}

// Prints:

1:inside map
5:inside foreach
2:inside map
6:inside foreach
3:inside map
7:inside foreach

Shouldn't the output be :

1:inside map
2:inside map
3:inside map
5:inside foreach
6:inside foreach
7:inside foreach

I was under the impression that after each intermediate operation ends, it returns a new stream. So foreach should have gotten (5,6,7) and therefore print

 5:inside foreach
 6:inside foreach
 7:inside foreach

But the case shows that foreach is executed for each entry of map one by one. Why is that?

microwth
  • 1,016
  • 1
  • 14
  • 27
  • That is a bit the point of streams. You're abstracting away this ordering. Which way do you think would be more efficient? – matt May 16 '23 at 12:56
  • can you elaborate? – microwth May 16 '23 at 13:01
  • Intermediate operations are lazy by default, and they don't process further elements until a final ones consumes the previous. The newly created stream is only queried when the final operation request to read the next element to process – jesantana May 16 '23 at 13:05
  • 2
    https://stackoverflow.com/a/35157305/2711488 – Holger May 16 '23 at 14:48

2 Answers2

5

That's because map() is indeed an intermediate operation, but it's also a stateless one. From the docs (https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps):

Intermediate operations are further divided into stateless and stateful operations. Stateless operations, such as filter and map, retain no state from previously seen element when processing a new element -- each element can be processed independently of operations on other elements. Stateful operations, such as distinct and sorted, may incorporate state from previously seen elements when processing new elements.

Meaning that whenever your element is mapped to a new one, it can immediately be processed by the rest of the pipeline, without the need to wait for all the elements to be mapped first. That's obviously an over-simplification, but I just wanted to explain the general idea.

Pateman
  • 2,727
  • 3
  • 28
  • 43
1

Streams guarantee order processes take, but not the order the elements will be processed. Consider your example as a loop. If it were to work as you are suggesting, it would look like two loops.

for(Element e: stream){
    e.setValue( e.getValue() + 4 );
}

for( Element e: stream){
    System.out.println(e.getValue());
}

In this case you might say, wouldn't it be more efficient to use one loop?

for( Element e: stream ){
    e.setValue( e.getValue() + 4 );
    System.out.println(e.getValue());
}

This re-ordering offers some advantages, such as parallel computing and processing infinite streams. It also lets you create fluent streams as opposed to a monolithic mapping process.

Stream<A> a = stream.map( b->{
                //perform
                //a
                //lot
                //of
                //operations.
                return b;});

Which could be split into a multiple calls without traversing the stream multiple times.

matt
  • 10,892
  • 3
  • 22
  • 34