在流中使用 Java 8 foreach 循环移动到下一个项目

问题描述 投票:0回答:5

我在尝试移动循环中的下一个项目时遇到 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!
}
java java-8
5个回答
447
投票

使用

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
语句的范围变得更加明显。


8
投票

另一个解决方案:使用相反的条件通过过滤器: 示例:

if(subscribtion.isOnce() && subscribtion.isCalled()){
                continue;
}

可以替换为

.filter(s -> !(s.isOnce() && s.isCalled()))

最直接的方法似乎是使用“return;”不过。


2
投票

您传递给

forEach()
的 lambda 会针对从流接收到的每个元素进行评估。迭代本身在 lambda 范围内不可见,因此您不能像
continue
是 C 预处理器宏那样
forEach()
。相反,您可以有条件地跳过其中的其余语句。


0
投票

您可以使用简单的

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;
语句中的谓词相反,因此只需使用
!
(逻辑非)。


0
投票

您可以使用

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
© www.soinside.com 2019 - 2024. All rights reserved.