英文:
order of execution of map().map() in Java streams
问题
在主函数中我使用了以下简化的代码:
主函数:
Foe[] foe = Stream.of(foe_2).map(Mapper_1).map(Mapper_2).toArray(Foe::new);
在 Mapper_1 中:
{
System.out.println("Mapper_1");
进行操作
}
在 Mapper_2 中:
{
System.out.println("Mapper_2");
进行其他操作
}
我需要确保 Mapper_1 在 Mapper_2 开始之前完成。由 Mapper_1 和 Mapper_2 调用的函数彼此不会相互调用!
预期的结果是:
Mapper_1 对 Stream.of(foe_2)
返回的流中的所有元素进行映射,然后 Mapper_2 对输出数组进行映射,得到以下输出:
Mapper_1
...
Mapper_1
Mapper_2
...
Mapper_2
实际上一直发生的情况是:
Mapper_1 对 Stream.of(foe_2)
的第一个元素进行映射,紧接着 Mapper_2 对这个第一个结果进行映射,准备将其放入输出数组中,然后 Mapper_1 对第二个元素进行映射,导致以下输出:
Mapper_1
Mapper_2
NullpointerExcpetion
我得到了这个异常是因为 Mapper_1 没有映射所有元素,导致数据结构混乱。
这是正常的行为吗?这对我来说很反直觉,还是我漏掉了什么?
英文:
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?
答案1
得分: 4
流(Streams)逐个元素进行处理,因此您所看到的行为实际上是 JDK 规范所要求的。
要通过 Mapper 1 逐个处理所有元素,然后通过 Mapper 2 处理所有元素,您需要连续使用两个流,以及在传递元素到第二个流之前需要收集第一个流中元素的某种方式。
但不用担心,您可以用一行代码来表达:
Foe[] foe = Stream.of(foe_2)
.map(Mapper_1)
.collect(toList()).stream()
.map(Mapper_2)
.toArray(Foe::new);
英文:
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)
答案2
得分: 1
你所看到的行为是预期的,甚至是必要的,因为流可能是无限的。仅当终端操作请求流生成项目时,流才会‘生成’项目,当这种情况发生时,整个步骤链将针对该个别项目执行。
因此,当执行toArray
时,它将要求流生成第一个项目,该项目经过Mapper_1
然后经过Mapper_2
,然后是第二个项目,该项目经过Mapper_1
然后经过Mapper_2
,依此类推。
如果你希望Mapper_1
在进入Mapper_2
之前看到所有项目,那么你需要在将其流式传输到Mapper_2
之前先收集到一个中间集合或数组中。
例如:
Foe[] foe = Stream.of(foe_2)
.map(Mapper_1)
.collect(Collectors.toList())
.stream().map(Mapper_2)
.toArray(Foe[]::new);
然而,如果你的映射器是无状态的,并且不依赖于查看所有项目,那将会更好。
另请参阅 https://stackoverflow.com/questions/29915591/java-8-stream-operations-execution-order
英文:
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 https://stackoverflow.com/questions/29915591/java-8-stream-operations-execution-order
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论