2

I have a set and a method:

private static Set<String> set = ...;
public static String method(){
    final String returnVal[] = new String[1];
    set.forEach((String str) -> {
        returnVal[0] += str;
        //if something: goto mark
    });
    //mark
    return returnVal[0];
}

Can I terminate the forEach inside the lambda (with or without using exceptions)? Should I use an anonymous class?

I could do this:

set.forEach((String str) -> {
     if(someConditions()){
         returnVal[0] += str;
     }
});

but it wastes time.

implementation using stream.reduce

return set.parallelStream().reduce((output, next) -> {
    return someConditions() ? next : output;
}).get(); //should avoid empty set before

I am looking for the fastest solution so exception and a 'real' for each loop are acceptable if they are fast enough.

Valen
  • 1,693
  • 1
  • 20
  • 17
  • 1
    Have you tried a return? – Elliott Frisch Jun 14 '14 at 02:59
  • 1
    return should just break the lambda – Valen Jun 14 '14 at 02:59
  • 1
    Please define *terminate*. – Elliott Frisch Jun 14 '14 at 03:00
  • stop the forEach loop, execute the following code – Valen Jun 14 '14 at 03:01
  • 2
    @Jeff: That applies to C#, and this is a different style altogether. I'm not convinced that answer could be used to answer this question. – Makoto Jun 14 '14 at 03:05
  • @Makoto thank you, I just think if the implementation in the set of forEach is a simple loop, I can't make any modification to that loop – Valen Jun 14 '14 at 03:07
  • What is it that you're trying to accomplish? I would expect that the `forEach` isn't a simple iteration, as its consumer is applied to all elements of the stream unconditionally. I imagine that, if you illustrate what it is you're attempting to accomplish, that there *may* be something you can use to express it that isn't exactly the same as a `forEach`. – Makoto Jun 14 '14 at 03:11
  • @Makoto actually, what I am doing is a map.forEach using BiConsumer, comparing the value/element and return something – Valen Jun 14 '14 at 03:13
  • (While the C# question/answer is fairly fitting, as C# doesn't have a "terminate" mechanism either, try not to cross-languages.) – user2864740 Jun 14 '14 at 03:31
  • 2
    Anyway, my two-cents is that `forEach` is a *terrible* pattern/concept as it is only useful for side-effects in a "stream" operation but it doesn't use/create any applicable stream result as `map` or `filter` do! I don't use `ForEach` in C# for this same reason, and won't use `forEach` in Java either: just stick with an "imperative" loop in this case - it shows the non-stream (or "side effect") intent better, along with the forcing of lazy sequences, and allows a standard `break` (or `return`) usage. – user2864740 Jun 14 '14 at 03:32

1 Answers1

6

I'm reluctant to answer this even though I'm not entirely sure what you're attempting to accomplish, but the simple answer is no, you can't terminate a forEach when it's halfway through processing elements.

The official Javadoc states that it is a terminal operation that applies against all elements in the stream.

Performs an action for each element of this stream.

This is a terminal operation.

If you want to gather the results into a single result, you want to use reduction instead.

Be sure to consider what it is a stream is doing. It is acting on all elements contained in it - and if it's filtered along the way, each step in the chain can be said to act on all elements in its stream, even if it's a subset of the original.


In case you were curious as to why simply putting a return wouldn't have any effect, here's the implementation of forEach.

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

The consumer is explicitly passed in, ad this is done independently of the actual iteration going on. I imagine you could throw an exception, but that would be tacky when more elegant solutions likely exist.

Makoto
  • 104,088
  • 27
  • 192
  • 230