【问题标题】:Move to next item using Java 8 foreach loop in stream在流中使用 Java 8 foreach 循环移至下一项
【发布时间】:2015-09-18 14:50:04
【问题描述】:

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

【问题讨论】:

  • 我需要继续循环中的下一项。
  • 他的意思是,使用您显示的代码,没有continue 仍然会在没有任何功能更改的情况下移动到下一个项目。
  • 如果目标是避免在调用 continue 之后执行行,并且您没有向我们展示,只需将所有这些行放在 else 块中。如果continue 之后没有任何内容,则删除 if 块并继续:它们没用。

标签: java java-8


【解决方案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);
    });
}

输出:

一个
c

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

【讨论】:

    【解决方案2】:

    另一种解决方案:使用反转条件进行过滤: 示例:

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

    可以替换为

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        您可以使用简单的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; 语句中的谓词正好相反,所以只需使用!(逻辑非)。

        【讨论】:

          猜你喜欢
          • 2018-11-19
          • 1970-01-01
          • 1970-01-01
          • 2013-05-14
          • 1970-01-01
          • 1970-01-01
          • 2023-03-10
          相关资源
          最近更新 更多