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

2022-08-31 06:22:39

我对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!
}

答案 1

使用将正常工作。它不会阻止整个循环完成。它只会停止执行循环的当前迭代。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
c

请注意迭代的执行方式,但在下一个迭代中打印就好了。return;bc

为什么会这样?

该行为起初似乎不直观的原因是,我们已经习惯了中断整个方法执行的语句。因此,在本例中,我们希望整个方法执行将停止。returnmain

但是,需要理解的是lambda表达式,例如:

str -> {
    if (str.equals("b")) return;

    System.out.println(str);
}

...确实需要被视为它自己独特的“方法”,与方法完全分开,尽管它位于其中的便利位置。因此,实际上,该语句仅停止 lambda 表达式的执行。mainreturn

需要理解的第二件事是:

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


答案 2

另一种解决方案:通过一个过滤器与你的倒置条件:示例:

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

可替换为

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

不过,最直接的方法似乎是使用“返回”。


推荐