2

I use the following code (simplified):
in main:

Foe[] foe = Stream.of(foe_2).map(Mapper_1).map(Mapper_2).toArray(Foe::new); 

in Mapper_1:

{ System.out.println("Mapper_1"); do stuff } 

in Mapper_2:

{ System.out.println("Mapper_2"); do other stuff } 

I need Mapper_1 to be finished before Mapper_2 starts.
The functions called by Mapper_1 and Mapper_2 do not call each other!

What I expected:
Mapper_1 maps all elements of the stream that Stream.of(foe_2) returns, then Mapper_2 maps to the output array, resulting in this output:

Mapper_1 ... Mapper_1 Mapper_2 ... Mapper_2 

What happens consistently:
Mapper_1 maps the first element of Stream.of(foe_2) and immediately after that, Mapper_2 takes this first result and maps it, ready to be put into the output array, before Mapper_1 maps the second element, resulting in this output:

Mapper_1 Mapper_2 NullpointerExcpetion 

I get the Exception because Mapper_1 didn't map all elements and the data-structure is messed up.

Is this normal behavior, which would occur counter-intuitive to me, or am I missing something?

3
  • Can you share some more details on what Mapper_1 and Mapper_2 are supposed to do? Commented Sep 20, 2020 at 16:28
  • Mapper_1 loads some data. Mapper_2 processes this data (builds kind of like a double linked list with 2 next and previous elements each), but needs all the data or else will run into nullpointers Commented Sep 20, 2020 at 16:30
  • 2
    Related: Java 8 stream operations execution order Commented Sep 20, 2020 at 16:33

2 Answers 2

4

Streams process 1 element at a time, so the behaviour you are seeing is in fact required by the JDK spec.

To process all elements through mapper 1, then all elements through mapper 2, you need 2 streams one after the other and something to collect the first stream in the middle before passing elements to the second stream.

But don’t worry; you can express it in one line:

Foe[] foe = Stream.of(foe_2) .map(Mapper_1) .collect(toList()).stream() .map(Mapper_2) .toArray(Foe::new) 
Sign up to request clarification or add additional context in comments.

Comments

1

The behaviour you see is expected and even necessary as streams are potentially infinite. A stream only 'produces' an item when it is asked for an item by a terminal operation, and when that happens the entire chain of steps is executed for that individual item.

So when toArray is executed, it will ask the stream to produce the first item, which is mapped through Mapper_1 and then Mapper_2, then the second item, which is mapped through Mapper_1 and then Mapper_2, etc.

If you need Mapper_1 to see all items before you go to Mapper_2, then you will need to collect into an intermediate collection or array before streaming that into Mapper_2.

For example

Foe[] foe = Stream.of(foe_2) .map(Mapper_1) .collect(Collectors.toList()) .stream().map(Mapper_2) .toArray(Foe[]::new); 

However, it would be better if your mappers are stateless and do not rely on seeing all items.

See also Java 8 stream operations execution order

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.