Break or return from Java 8 stream forEach?
Asked Answered
T

15

430

When using external iteration over an Iterable we use break or return from enhanced for-each loop as:

for (SomeObject obj : someObjects) {
   if (some_condition_met) {
      break; // or return obj
   }
}

How can we break or return using the internal iteration in a Java 8 lambda expression like:

someObjects.forEach(obj -> {
   //what to do here?
})
Thant answered 26/4, 2014 at 7:47 Comment(4)
You can't. Just use a real for statement.Venal
possible duplicate of Java 8: Limit infinite stream by a predicateOlivas
Consider another approach, you just want to not execute code, so, a simple if condition inside the forEach will do the trick.Marketing
With Java 9 per #20746929Decern
T
480

If you need this, you shouldn't use forEach, but one of the other methods available on streams; which one, depends on what your goal is.

For example, if the goal of this loop is to find the first element which matches some predicate:

Optional<SomeObject> result =
    someObjects.stream().filter(obj -> some_condition_met).findFirst();

(Note: This will not iterate the whole collection, because streams are lazily evaluated - it will stop at the first object that matches the condition).

If you just want to know if there's an element in the collection for which the condition is true, you could use anyMatch:

boolean result = someObjects.stream().anyMatch(obj -> some_condition_met);
Toile answered 26/4, 2014 at 7:54 Comment(10)
This works if finding an object was the goal, but that goal is usually served by a return statement from a findSomething method. break is more usually associated with a take while-kind of operation.Besnard
@MarkoTopolnik Yes, the original poster has not given us sufficient information to know what exactly the goal is; a "take while" is a third possibility besides the two I mentioned. (Is there a simple way to do "take while" with streams?).Toile
What about when the goal is to properly implement cancel behavior? Is the best thing we can do just to throw a runtime exception inside the forEach lambda when we notice that the user requested a cancel?Deferred
@HonzaZidek Edited, but the point is not whether it's possible or not, but what the right way is to do things. You shouldn't try to force using forEach for this; you should use another, more appropriate method instead.Toile
@Toile I agree with you, I wrote that I did not like the "Exception solution". However your wording "This is not possible with forEach" was technically incorrect. I also prefer your solution, however I can imagine use cases where the solution provided in my answer is preferable: when the loop should be ended because of a real exception. I agree that you should not generally use the exceptions to control the flow.Mccammon
@Jesper: Perhaps a lazy combination of filter() followed by limit() might emulate take while?Whitish
Note for strangers: do not confuse between allMatch and anyMatch, which is dependenced on your goal.Spathe
@Toile flawless answer!Upu
@Jesper, what if I want to break for business requirements sake?Tatro
@ArtanisZeratul When you work with streams, you have to think in a different way than when you are working with loops; that's functional programming style vs. procedural programming style. It has nothing to do with business requirements; just with technical coding.Toile
S
85

A return in a lambda equals a continue in a for-each, but there is no equivalent to a break. You can just do a return to continue:

someObjects.forEach(obj -> {
   if (some_condition_met) {
      return;
   }
})
Signore answered 25/4, 2016 at 14:42 Comment(3)
Nice and idiomatic solution to address the general requirement. The accepted answer extrapolates the requirement.Hobie
in other words this does not "Break or return from Java 8 stream forEach" which was the actual questionDeist
This will still "pull" records through the source stream though, which is bad if you're paging through some sort of remote dataset.Heder
M
59

This is possible for Iterable.forEach() (but not reliably with Stream.forEach()). The solution is not nice, but it is possible.

WARNING: You should not use it for controlling business logic, but purely for handling an exceptional situation which occurs during the execution of the forEach(). Such as a resource suddenly stops being accessible, one of the processed objects is violating a contract (e.g. contract says that all the elements in the stream must not be null but suddenly and unexpectedly one of them is null) etc.

According to the documentation for Iterable.forEach():

Performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception... Exceptions thrown by the action are relayed to the caller.

So you throw an exception which will immediately break the internal loop.

The code will be something like this - I cannot say I like it but it works. You create your own class BreakException which extends RuntimeException.

try {
    someObjects.forEach(obj -> {
        // some useful code here
        if(some_exceptional_condition_met) {
            throw new BreakException();
       }
    }
}
catch (BreakException e) {
    // here you know that your condition has been met at least once
}

Notice that the try...catch is not around the lambda expression, but rather around the whole forEach() method. To make it more visible, see the following transcription of the code which shows it more clearly:

Consumer<? super SomeObject> action = obj -> {
    // some useful code here
    if(some_exceptional_condition_met) {
        throw new BreakException();
    }
});

try {
    someObjects.forEach(action);
}
catch (BreakException e) {
    // here you know that your condition has been met at least once
}
Mccammon answered 14/9, 2015 at 13:59 Comment(6)
I think this is a bad practice and should not be considered as a solution to the problem. It is dangerous as it could be misleading for a beginner. According to Effective Java 2nd Edition, Chapter 9, Item 57 : ' Use exceptions only for exceptional conditions'. Furthermore 'Use runtime exceptions to indicate programming errors'. In definitive, I strongly encourage anyone considering this solution to look into @Toile solution.Daph
@LouisF. I explicitly said "I cannot say I like it but it works". The OP asked "how to break from forEach()" and this is an answer. I fully agree that this should not be used to control the business logic. However I can imagine some useful use cases, like that a connection to a resource suddenly not available in the middle of forEach() or so, for which using exception is not bad practice. I have added a paragraph to my answer to be clear.Mccammon
I think this is a fine solution. After searching Google for "java exceptions" and other searches with a few more words like "best practices" or "unchecked", etc., I see there is controversy over how to use exceptions. I used this solution in my code because the stream was performing a map that would take minutes. I wanted the user to be able to cancel the task so I checked at the beginning of each calculation for the flag "isUserCancelRequested" and threw an exception when true. It's clean, the exception code is isolated to small portion of the code, and it works.Gyrostatics
In that situation maybe it's better to put the long operation in a thread. Then just cancel the thread and discard the result.Nip
Note that Stream.forEach does not provide the same strong guarantee about exceptions being relayed to the caller, so throwing an exception isn't guaranteed to work this way for Stream.forEach.Abhenry
@Abhenry That is a valid point, thanks. The original post was about Iterable.forEach(), but I added your point to my text just for the completeness.Mccammon
C
31

Below you find the solution I used in a project. Instead forEach just use allMatch:

someObjects.allMatch(obj -> {
    return !some_condition_met;
});
Cumquat answered 30/10, 2014 at 8:52 Comment(1)
I think this pretty much what I was looking for.Fluker
T
25

Update with Java 9+ with takeWhile:

MutableBoolean ongoing = MutableBoolean.of(true);
someobjects.stream()...takeWhile(t -> ongoing.value()).forEach(t -> {
    // doing something.
    if (...) { // want to break;
        ongoing.setFalse();
    }
});
Trusting answered 3/12, 2016 at 1:50 Comment(2)
As from a previous answer, This requires Java 9 . The OP specifically asked about java 8Lickspittle
The .takeWhile call should be directly after .stream() so the stream stops directly after ongoing is set to falseDonelu
D
13

Either you need to use a method which uses a predicate indicating whether to keep going (so it has the break instead) or you need to throw an exception - which is a very ugly approach, of course.

So you could write a forEachConditional method like this:

public static <T> void forEachConditional(Iterable<T> source,
                                          Predicate<T> action) {
    for (T item : source) {
        if (!action.test(item)) {
            break;
        }
    }
}

Rather than Predicate<T>, you might want to define your own functional interface with the same general method (something taking a T and returning a bool) but with names that indicate the expectation more clearly - Predicate<T> isn't ideal here.

Deedradeeds answered 26/4, 2014 at 7:49 Comment(3)
I'd suggest that actually using the Streams API and a functional approach here is preferable over creating this helper method if Java 8 is being used anyhow.Ramonaramonda
This is the classic takeWhile operation, and this question is but one of those demonstrating how much its lack in the Streams API is felt.Besnard
@Marko: takeWhile feels more like it would be an operation yielding items, not performing an action on each. Certainly in LINQ in .NET it would be poor form to use TakeWhile with an action with side-effects.Deedradeeds
T
10

You can use java8 + rxjava.

//import java.util.stream.IntStream;
//import rx.Observable;

    IntStream intStream  = IntStream.range(1,10000000);
    Observable.from(() -> intStream.iterator())
            .takeWhile(n -> n < 10)
            .forEach(n-> System.out.println(n));
Tenuis answered 2/6, 2015 at 18:0 Comment(1)
Java 9 will offer support for takeWhile operation on streams.Deandra
B
7

For maximal performance in parallel operations use findAny() which is similar to findFirst().

Optional<SomeObject> result =
    someObjects.stream().filter(obj -> some_condition_met).findAny();

However If a stable result is desired, use findFirst() instead.

Also note that matching patterns (anyMatch()/allMatch) will return only boolean, you will not get matched object.

Burnejones answered 2/12, 2014 at 5:17 Comment(0)
C
6
public static void main(String[] args) {
    List<String> list = Arrays.asList("one", "two", "three", "seven", "nine");
    AtomicBoolean yes = new AtomicBoolean(true);
    list.stream().takeWhile(value -> yes.get()).forEach(value -> {
        System.out.println("prior cond" + value);
        if (value.equals("two")) {
            System.out.println(value);
            yes.set(false);
        }

    });
    //System.out.println("Hello World");
}
Creatine answered 5/2, 2021 at 10:50 Comment(3)
Above code using takeWhile method of java 9 and an extra variable to track the condition works perfectly fine for me.Creatine
The community encourages adding explanations alongisde code, rather than purely code-based answers (see here).Darrel
Hello and welcome to SO! While this code may answer the question, providing additional context regarding how and/or why it solves the problem would improve the answer's long-term value. Please read the tour, and How do I write a good answer?Schadenfreude
K
3

I have achieved by something like this

  private void doSomething() {
            List<Action> actions = actionRepository.findAll();
            boolean actionHasFormFields = actions.stream().anyMatch(actionHasMyFieldsPredicate());
            if (actionHasFormFields){
                context.addError(someError);
            }
        }
    }

    private Predicate<Action> actionHasMyFieldsPredicate(){
        return action -> action.getMyField1() != null;
    }
Kinchen answered 24/6, 2015 at 9:50 Comment(0)
H
3

You can achieve that using a mix of peek(..) and anyMatch(..).

Using your example:

someObjects.stream().peek(obj -> {
   <your code here>
}).anyMatch(obj -> !<some_condition_met>);

Or just write a generic util method:

public static <T> void streamWhile(Stream<T> stream, Predicate<? super T> predicate, Consumer<? super T> consumer) {
    stream.peek(consumer).anyMatch(predicate.negate());
}

And then use it, like this:

streamWhile(someObjects.stream(), obj -> <some_condition_met>, obj -> {
   <your code here>
});
Hoo answered 8/11, 2016 at 14:59 Comment(1)
The anyMatch will not stop the first call to peek. If you want to do something in the peek lamda only if "some_condition_met" is true, you will have to put an if statement in the peek lamda to do something only if "some_condition_met" is true.Cincture
M
1
int valueToMatch = 7;
Stream.of(1,2,3,4,5,6,7,8).anyMatch(val->{
   boolean isMatch = val == valueToMatch;
   if(isMatch) {
      /*Do whatever you want...*/
       System.out.println(val);
   }
   return isMatch;
});

It will do only operation where it find match, and after find match it stop it's iteration.

Mackintosh answered 10/2, 2020 at 5:16 Comment(0)
R
1

You would use .map for this and return a boolean to indicate a break

someObjects.map(obj -> {
   if (some_condition_met) {
      return true;
   }
   return false;    
}).filter(Boolean.booleanValue).findAny();

findAny() is a short-circuiting terminal operation, thus the moment the filter passes an item (true) findFirst will break the operation.

An even shorter operation would be calling anyMatch instead of map as below:

someObjects.anyMatch(obj -> {
       if (some_condition_met) {
          return true;
       }
       return false;    
    });

as anyMatch is also a short-circuit operation.

Reticulation answered 7/4, 2023 at 18:35 Comment(0)
M
0

What about this one:

final BooleanWrapper condition = new BooleanWrapper();
someObjects.forEach(obj -> {
   if (condition.ok()) {
     // YOUR CODE to control
     condition.stop();
   }
});

Where BooleanWrapper is a class you must implement to control the flow.

Marketing answered 4/1, 2018 at 12:15 Comment(3)
Or AtomicBoolean?Neologize
This skips the object processing when !condition.ok(), however it doesn't prevent forEach() from looping over all the objects anyway. If the slow part is the forEach() iteration and not its consumer (eg, it gets objects from a slow network connection), then this approach isn't very useful.Cawnpore
Yes you are right, my answer is quite wrong in this case.Marketing
M
0

I would suggest using anyMatch. Example:-

return someObjects.stream().anyMatch(obj -> 
    some_condition_met;
);

You can refer this post for understanding anyMatch:- https://beginnersbook.com/2017/11/java-8-stream-anymatch-example/

Microphysics answered 30/3, 2020 at 18:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.