Pages

Wednesday, May 13, 2020

How to Break or return from Java Stream forEach in Java 8

1. Introduction


In this tutorial, You'll learn how to use a break or return in Java 8 Streams when working with the forEach() method.

Java 8 forEach() method takes consumer that will be running for all the values of Stream.

Once forEach() method is invoked then it will be running the consumer logic for each and every value in the stream from a first value to last value.

java 8 custom forEach() methd for Stream API


For each keeps the code very clean and in a declarative manner.

But, when you working with a traditional loop, you can use a break or return from loop based on a condition.

How to Break or return from Java Stream forEach


But, you might have noticed that missing equivalent break or return statement to stop the loop execution based on a condition.

If the stream is an extremely long one then you need to traverse till last value to end the loop.

Our requirement is to stop the loop once out condition is met then forEach does not much help on this.

There are few ways to do even using Java 8 and java 9 API techniques or methods.


2. Java 8 Stream filter() - does not work

Java 8 Break or Return Stream forEach


filter() method which does not work well in our case.

Let us write an example program using filter() method on the stream of Strings.

Print the values until its length matches to greater than 3. If it founds match then skip the remaining.

First of all, we'll show you how to do with stream and then normal for loop example.


package com.javaprogramto.java8.streams.foreach;

import java.util.Arrays;
import java.util.List;

public class StreamFilterBreak {

    public static void main(String[] args) {

        List<String> list = Arrays.asList("one", "two", "three", "seven", "nine", "ten");

        list.stream().filter(s -> s.length() <= 3).forEach(value -> System.out.println(value));
    }
}

Output:

[one
two
ten]

Actually, it should print only the "one", "two" and not print "ten".

But this approach does not work when we try to replace this with the normal for a loop as below.

package com.javaprogramto.java8.streams.foreach;

import java.util.Arrays;
import java.util.List;

public class StreamForBreak {

    public static void main(String[] args) {

        List<String> list = Arrays.asList("one", "two", "three", "seven", "nine", "ten");

        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).length() > 3) {

                break;
            }
            System.out.println(list.get(i));

        }

    }
}

Output:

[one
two]

This code works well but with the usage of stream filter() method which does not fulfill our requirement.

3. Using java 9 Stream.takeWhile()


Further, Java 9 API has equipped with a developer handy method to avoid running unnecessary loops once our condition is met.

New stream api introduced with takeWhile(Predicate<T> p) method which stops once the condition is met then it stops iterating through the remaining elements. So, it returns a new Stream until the condition is met.

This method same as break or return statement in java 9 API.

package com.javaprogramto.java8.streams.foreach;

import java.util.Arrays;
import java.util.List;

public class StreamTakeWhile {

    public static void main(String[] args) {

        List<String> list = Arrays.asList("one", "two", "three", "seven", "nine");

        list.stream().takeWhile(value -> value.length() > 3).forEach(value -> {
            if (value.length() > 3) {
                System.out.println(value);
            }
        });
    }
}

This code produces the following output of what we are interested in.

[one
two]

If you are not using java 9 version then it is out of the box for you to use. So let us move to the next section.

4. Custom forEach() loop in java 8


Additionally, We can achieve the same with the help of custom foreach concept.

Let us build the custom forEach() method that works the same as takeWhile().

This is implemented using SplitIterator.tryAdvance() method.

package com.javaprogramto.java8.streams.foreach;

import java.util.Spliterator;
import java.util.function.BiConsumer;
import java.util.stream.Stream;

public class CustomForEachImpl {

    public static class Break {
        private boolean doBreak = false;

        public void stop() {
            doBreak = true;
        }

        boolean get() {
            return doBreak;
        }
    }

    public static <T> void forEach(Stream<T> stream, BiConsumer<T, Break> consumer) {
        Spliterator<T> spliterator = stream.spliterator();
        boolean hadNext = true;
        Break breaker = new Break();

        while (hadNext && !breaker.get()) {
            hadNext = spliterator.tryAdvance(elem -> {
                consumer.accept(elem, breaker);
            });
        }
    }
}

Use our new forEach() method from CustomForEachImpl class.

This is a little tricky but if you are strong in core java then you can easily understand the logic inside out custom forEach() method.

Especially, the custom forEach() method takes two arguments.

The first argument is Stream<T> and the second is BiConsumer which takes T and Break instance.

This BiConsumer will play a crucial role in when you call this method

Let us jump into, how can you call this with example.

The CustomForEach logic can be used for return statement also. One the condition is met then stop or skip the next all values in the stream. This will reduce the overhead in processing very large streams.

package com.javaprogramto.java8.streams.foreach;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class CustomForEachExample {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("one", "two", "three", "seven", "nine", "ten");

        List<String> output = new ArrayList<>();
        CustomForEachImpl.forEach(list.stream(), (str, breaker) -> {

            if (str.length() > 3) {
                breaker.stop();
            } else {
                output.add(str);
            }

        });

        System.out.println("custom foreach output: "+output);

    }
}

Output

[custom foreach output: [one, two]]

5. Conclusion


In conclusion, you've seen how can we take forEach to next advance level with the help of SplitIterator to get the functionality of break or return in-stream forEach() loop.

Building the Custom ForEach() method with an example.

Example with Java 9 Stream api takeWhile() method.


All the code is shown in this article is over GitHub.

You can download the project directly and can run in your local without any errors.



If you have any queries please post in the comment section.

No comments:

Post a Comment

Please do not add any spam links in the comments section.