我在尝试移动循环中的下一个项目时遇到 Java 8 foreach 流的问题。我无法设置像
continue;
这样的命令,只有 return;
有效,但在这种情况下你将退出循环。我需要继续循环中的下一个项目。我怎样才能做到这一点?
示例(不起作用):
try(Stream<String> lines = Files.lines(path, StandardCharsets.ISO_8859_1)){
filteredLines = lines.filter(...).foreach(line -> {
...
if(...)
continue; // this command doesn't working here
});
}
示例(工作):
try(Stream<String> lines = Files.lines(path, StandardCharsets.ISO_8859_1)){
filteredLines = lines.filter(...).collect(Collectors.toList());
}
for(String filteredLine : filteredLines){
...
if(...)
continue; // it's working!
}
使用
return;
效果很好。它不会阻止完整循环的完成。它只会停止执行 forEach
循环的当前迭代。
尝试下面的小程序:
public static void main(String[] args) {
ArrayList<String> stringList = new ArrayList<>();
stringList.add("a");
stringList.add("b");
stringList.add("c");
stringList.stream().forEach(str -> {
if (str.equals("b")) return; // only skips this iteration.
System.out.println(str);
});
}
输出:
a
注意
return;
是如何在 b
迭代中执行的,但是 c
在接下来的迭代中打印得很好。
这种行为一开始看起来不直观,是因为我们习惯了
return
语句中断整个方法的执行。因此,在这种情况下,我们预计 main
方法执行将整体停止。
但是,需要理解的是,lambda表达式,如:
str -> {
if (str.equals("b")) return;
System.out.println(str);
}
...确实需要被视为它自己独特的“方法”,与
main
方法完全分开,尽管它位于其中很方便。所以实际上,return
语句只会停止 lambda 表达式的执行。
第二个需要明白的是:
stringList.stream().forEach()
...实际上只是一个普通的循环,每次迭代都会执行 lambda 表达式。
记住这两点,上面的代码可以用以下等效方式重写(仅用于教育目的):
public static void main(String[] args) {
ArrayList<String> stringList = new ArrayList<>();
stringList.add("a");
stringList.add("b");
stringList.add("c");
for(String s : stringList) {
lambdaExpressionEquivalent(s);
}
}
private static void lambdaExpressionEquivalent(String str) {
if (str.equals("b")) {
return;
}
System.out.println(str);
}
有了这个“不那么神奇”的等效代码,
return
语句的范围变得更加明显。
另一个解决方案:使用相反的条件通过过滤器: 示例:
if(subscribtion.isOnce() && subscribtion.isCalled()){
continue;
}
可以替换为
.filter(s -> !(s.isOnce() && s.isCalled()))
最直接的方法似乎是使用“return;”不过。
您传递给
forEach()
的 lambda 会针对从流接收到的每个元素进行评估。迭代本身在 lambda 范围内不可见,因此您不能像 continue
是 C 预处理器宏那样 forEach()
。相反,您可以有条件地跳过其中的其余语句。
您可以使用简单的
if
语句来代替 continue。因此,您可以尝试:,而不是使用代码的方式
try(Stream<String> lines = Files.lines(path, StandardCharsets.ISO_8859_1)){
filteredLines = lines.filter(...).foreach(line -> {
...
if(!...) {
// Code you want to run
}
// Once the code runs, it will continue anyway
});
}
if 语句中的谓词将与
if(pred) continue;
语句中的谓词相反,因此只需使用 !
(逻辑非)。
您可以使用
takeWhile
lambda 函数。
如果此流是有序的,则返回一个流,该流由从此流中获取的与给定谓词匹配的元素的最长前缀组成。否则,如果该流是无序的,则返回一个由从此流中获取的与给定谓词匹配的元素子集组成的流。
public class TestFilterLoop {
public static void main(String[] args) {
ArrayList<String> stringList = new ArrayList<>();
stringList.add("a");
stringList.add("b");
stringList.add("c");
stringList.add("d");
stringList.stream()
.takeWhile(str -> !str.equals("c"))
.forEach(System.out::println);
}
}
仅打印直到字符串不是
c
。所以它会返回并且不会继续循环。输出:
a
b