Move to next item using Java 8 foreach loop in stream

JavaJava 8

Java Problem Overview


I have a problem with the stream of Java 8 foreach attempting to move on next item in loop. I cannot set the command like continue;, only return; works but you will exit from the loop in this case. I need to move on next item in loop. How can I do that?

Example(not working):

try(Stream<String> lines = Files.lines(path, StandardCharsets.ISO_8859_1)){
            filteredLines = lines.filter(...).foreach(line -> {
           ...
           if(...)
              continue; // this command doesn't working here
    });
}

Example(working):

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 Solutions


Solution 1 - Java

Using return; will work just fine. It will not prevent the full loop from completing. It will only stop executing the current iteration of the forEach loop.

Try the following little program:

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

Output:

> a
> c

Notice how the return; is executed for the b iteration, but c prints on the following iteration just fine.

Why does this work?

The reason the behavior seems unintuitive at first is because we are used to the return statement interrupting the execution of the whole method. So in this case, we expect the main method execution as a whole to be halted.

However, what needs to be understood is that a lambda expression, such as:

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

    System.out.println(str);
}

... really needs to be considered as its own distinct "method", completely separate from the main method, despite it being conveniently located within it. So really, the return statement only halts the execution of the lambda expression.

The second thing that needs to be understood is that:

stringList.stream().forEach()

... is really just a normal loop under the covers that executes the lambda expression for every iteration.

With these 2 points in mind, the above code can be rewritten in the following equivalent way (for educational purposes only):

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

With this "less magic" code equivalent, the scope of the return statement becomes more apparent.

Solution 2 - Java

Another solution: go through a filter with your inverted conditions : Example :

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

can be replaced with

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

The most straightforward approach seem to be using "return;" though.

Solution 3 - Java

The lambda you are passing to forEach() is evaluated for each element received from the stream. The iteration itself is not visible from within the scope of the lambda, so you cannot continue it as if forEach() were a C preprocessor macro. Instead, you can conditionally skip the rest of the statements in it.

Solution 4 - Java

You can use a simple if statement instead of continue. So instead of the way you have your code, you can try:

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

The predicate in the if statement will just be the opposite of the predicate in your if(pred) continue; statement, so just use ! (logical not).

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionViktor M.View Question on Stackoverflow
Solution 1 - JavasstanView Answer on Stackoverflow
Solution 2 - JavaAnarkopsykotikView Answer on Stackoverflow
Solution 3 - JavaJohn BollingerView Answer on Stackoverflow
Solution 4 - Javauser377628View Answer on Stackoverflow