使用流中的Java 8 foreach循环移至下一项


126

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

请发布完整但最少的示例,以便于回答。
Tunaki

我需要循环移动下一个项目。
Viktor V.

2
他的观点是,使用您显示的代码,即使continue不进行任何功能更改,也不会将其移至下一个项目。
DoubleDouble 2015年

如果我们的目标是避免在通话继续后执行任何行,并且您没有向我们展示,则只需将所有这些行放在一个else块中即可。如果之后没有任何内容continue,则删除if块并继续执行:它们无用。
JB Nizet

Answers:


277

使用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);
    });
}

输出:

一个
ç

请注意,如何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语句的范围就变得更加明显。


3
我已经尝试过这种方法,但是为什么它不起作用,我又重复了一次这个技巧,并且它起作用了。谢谢,对我有帮助。似乎我只是劳累过度=)
Viktor V.

2
奇迹般有效!您能否添加一个解释为什么行得通呢?
sparkyspider17年

2
@Spider:已添加。
2004年

5

另一个解决方案:在条件相反的情况下通过过滤器:示例:

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

可以替换为

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

最直接的方法似乎是使用“返回”;虽然。


2

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


0

您可以使用简单的if语句代替继续。因此,您可以尝试:

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.