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

sxpgvts3  于 2023-09-29  发布在  Java
关注(0)|答案(5)|浏览(189)

我遇到了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!
}
kx1ctssn

kx1ctssn1#

使用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语句的作用域就变得更加明显了。

rseugnpd

rseugnpd2#

另一个解决方案:通过一个过滤器,用你的反转条件:范例:

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

可以替换为

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

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

41zrol4v

41zrol4v3#

传递给forEach()的lambda将对从流中接收的每个元素进行求值。迭代本身在lambda范围内是不可见的,所以你不能像forEach()是一个C预处理器宏那样continue它。相反,您可以有条件地跳过其中的其余语句。

yeotifhr

yeotifhr4#

您可以使用一个简单的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语句中的 predicate 将与if(pred) continue;语句中的 predicate 相反,因此只需使用!(逻辑非)。

k10s72fa

k10s72fa5#

可以使用takeWhile lambda函数。
如果此流是有序的,则返回一个流,该流由取自此流的与给定 predicate 匹配的元素的最长前缀组成。否则,如果此流是无序的,则返回一个流,该流由取自此流的与给定 predicate 匹配的元素的子集组成。

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

相关问题