有人可以解释一下为什么第一个代码示例没有保存我在地图上使用.map所做的更改,但是第二个代码示例呢?
第一个代码示例:
stringIntegerMap.entrySet().stream()
.map(element -> element.setValue(100));
第二个代码示例:
stringIntegerMap.entrySet().stream()
.map(element -> element.setValue(100))
.forEach(System.out::println);
另外,为什么第二个代码示例只打印值而不是整个元素(键+值)?
答案 0 :(得分:7)
您的流操作是惰性评估的。
如果您不调用forEach
(或collect
等)等终端操作,则实际上不会发生流式传输,因此您的setValue
不会被执行。
请注意,通常建议您修改正在流式传输的集合/地图。
最后,Map.Entry#setValue
的API为here。
您会注意到该方法返回:
与条目
对应的旧值
因此,当您执行映射操作时,生成的流包含值。
某些来源here(搜索"流操作和流水线"以及有关"非干扰&#34的部分可能有所帮助)。
答案 1 :(得分:1)
Streams由源,中间操作和终端操作组成。
终端操作通过从源中懒洋洋地收集元素,然后应用中间操作并最终执行终端操作来开始管道处理。
Stream.map
是一个中间操作,而Stream.forEach
是终端。因此,在您的第一个代码段中,管道处理永远不会启动(因此中间操作永远不会执行),因为没有终端操作。在第二个片段中使用forEach
时,将处理所有管道。
请查看java.util.stream
package文档,其中包含有关流的大量信息以及如何正确使用它们(即您不应在中间或最终操作中修改流的来源,例如您'在Stream.map
)。
修改强>
关于你的最后一个问题:
为什么第二个代码示例只打印值而不是整个元素(键+值)?
Mena's answer解释得很好:Map.Entry.setValue
不仅为条目设置了给定值,还返回旧值。当您在Map.Entry.setValue
中间操作中的lambda表达式中使用Stream.map
时,实际上是在将流的每个Map.Entry
元素转换为设置新值之前的值。因此,到达Stream.forEach
的是地图的旧值,而地图则通过Map.Entry.setValue
生成的副作用设置新值。