在Java 8中,Stream.map()和Stream.flatMap()方法有什么区别?
我有以下示例代码:
System.out.println(
"Result: " +
Stream.of(1, 2, 3)
.filter(i -> {
System.out.println(i);
return true;
})
.findFirst()
.get()
);
System.out.println("-----------");
System.out.println(
"Result: " +
Stream.of(1, 2, 3)
.flatMap(i -> Stream.of(i - 1, i, i + 1))
.flatMap(i -> Stream.of(i - 1, i, i + 1))
.filter(i -> {
System.out.println(i);
return true;
})
.findFirst()
.get()
);
Run Code Online (Sandbox Code Playgroud)
输出如下:
1
Result: 1
-----------
-1
0
1
0
1
2
1
2
3
Result: -1
Run Code Online (Sandbox Code Playgroud)
从这里我看到,在第一种情况下stream真的表现得懒惰 - 我们使用findFirst()所以一旦我们有第一个元素我们的过滤lambda没有被调用.然而,在使用flatMaps的第二种情况下,我们看到尽管找到满足过滤条件的第一个元素(它只是任何第一个元素,因为lambda总是返回true),流的其他内容仍然通过过滤函数被馈送.
我试图理解为什么它表现得像这样,而不是在第一个元素计算后放弃,如第一种情况.任何有用的信息将不胜感激.
我有一个昂贵的方法,我只想在流中必要时调用它。这是一个例子:
public static Optional<MyObject> findTarget(String input, List<MyObject> myList) {
return Stream.concat(myList.stream(), expensive().stream()).filter(o -> o.hasName(input)).findFirst();
}
Run Code Online (Sandbox Code Playgroud)
目标是根据值MyObject找到目标,但如果它不在ONLY 中,那么它将调用返回更大的列表并从那里查找。myListinputmyListexpensive()
上面的示例没有这样做,因为它似乎在Stream.concat消耗expensive()所有myList.
我能想到的一个丑陋的解决方案是分两步完成,例如:
return myList.stream().filter(o -> o.hasName(input)).findFirst().or(
() -> expensive().stream().filter(o -> o.hasName(input)).findFirst());
Run Code Online (Sandbox Code Playgroud)
但随后我将不得不重复过滤器和其余部分两次。
有没有更好的解决方案,甚至是单个 Stream 衬里可以做到这一点?
关于函数式编程,特别是新的Java 8流API,有太多的炒作.它被宣传为旧的良好循环和命令式范例的良好替代品.事实上,有时它看起来很不错并且做得很好.但性能呢?
例如,这是关于此的好文章:Java 8:没有更多循环 使用循环,您可以通过一次迭代完成所有工作.但是使用新的流API,您将链接多个循环,这使得它更慢(是不是?).看看他们的第一个样本.在大多数情况下,Loop甚至不会遍历整个阵列.但是,要使用新的流API进行过滤,您必须遍历整个数组以过滤掉所有候选项,然后您将能够获得第一个.
在这篇文章中提到了一些懒惰:
我们首先使用filter操作来查找具有Java标记的所有文章,然后使用findFirst()操作来获取第一个匹配项.由于流是惰性的并且过滤器返回流,因此该方法仅处理元素,直到找到第一个匹配.
作者对这种懒惰的意思是什么?
我做了简单的测试,它表明旧的良好循环解决方案可以快速运行10倍然后流式处理.
public void test() {
List<String> list = Arrays.asList(
"First string",
"Second string",
"Third string",
"Good string",
"Another",
"Best",
"Super string",
"Light",
"Better",
"For string",
"Not string",
"Great",
"Super change",
"Very nice",
"Super cool",
"Nice",
"Very good",
"Not yet string",
"Let's do the string",
"First string",
"Low string",
"Big bunny",
"Superstar",
"Last");
long start = System.currentTimeMillis();
for (int i = 0; i < 100000000; i++) {
getFirstByLoop(list);
}
long end = …Run Code Online (Sandbox Code Playgroud)