Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to iterate with foreach loop over java 8 stream

Suppose we try to apply to java 8 stream a lambda that could throw checked exception:

Stream<String> stream = Stream.of("1", "2", "3");
Writer writer = new FileWriter("example.txt");

stream.forEach(s -> writer.append(s)); // Unhandled exception: java.io.IOException

This won't compile.

One workaround is to nest checked exception in RuntimeException but it complicates later exception handling and it's just ugly:

stream.forEach(s -> {
    try {
        writer.append(s);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
});

Alternative workaround could be to convert limited functional forEach to plain old foreach loop that is more friendly to checked exceptions.

But naive approaches fail:

for (String s : stream) { // for-each not applicable to expression type 'java.util.stream.Stream<java.lang.String>'
    writer.append(s);
}

for (String s : stream.iterator()) { // foreach not applicable to type 'java.util.Iterator<java.lang.String>'
    writer.append(s);
}

Update

A trick that answers this question was previosly posted at Why does Stream<T> not implement Iterable<T>? as side answer that doesn't really answer that question itself. I think this is not enough to qualify this question as duplicate of that one because they ask different things.

like image 293
Vadzim Avatar asked Jul 29 '15 14:07

Vadzim


People also ask

Can we use forEach in streams Java?

Stream forEach() method in Java with examplesStream forEach(Consumer action) performs an action for each element of the stream. Stream forEach(Consumer action) is a terminal operation i.e, it may traverse the stream to produce a result or a side-effect.

Which method is used to iterate over each element of the stream?

Java Stream forEach() method is used to iterate over all the elements of the given Stream and to perform an Consumer action on each element of the Stream.

How do I loop through a stream?

List interface provides a stream() method which gives a stream to iterate using forEach method. In forEach method, we can use the lambda expression to iterate over all elements. The following code snippet shows the usage of streams to iterate over the list.

What is the purpose of forEach method of stream in Java 8?

The forEach method was introduced in Java 8. It provides programmers a new, concise way of iterating over a collection. The forEach method performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception.


Video Answer


4 Answers

By definition foreach loop requires an Iterable to be passed in.

It can be achieved with anonymous class:

    for (String s : new Iterable<String>() {
        @Override
        public Iterator<String> iterator() {
            return stream.iterator();
        }
    }) {
        writer.append(s);
    }

This can be simplified with lambda because Iterable is a functional interface:

    for (String s : (Iterable<String>) () -> stream.iterator()) {
        writer.append(s);
    }

This can be converted to method reference:

    for (String s : (Iterable<String>) stream::iterator) {
        writer.append(s);
    }

Explicit cast can be avoided with intermediate variable or method param:

    Iterable<String> iterable = stream::iterator;
    for (String s : iterable) {
        writer.append(s);
    }

There is also StreamEx library in maven central that features iterable streams and other perks out of the box.


Here are some most popular questions and approaches that provide workarounds on checked exception handling within lambdas and streams:

Java 8 Lambda function that throws exception?

Java 8: Lambda-Streams, Filter by Method with Exception

How can I throw CHECKED exceptions from inside Java 8 streams?

Java 8: Mandatory checked exceptions handling in lambda expressions. Why mandatory, not optional?

jOOλ Unchecked

Lombok @SneakyThrows

Kotlin ;)

like image 187
Vadzim Avatar answered Oct 12 '22 03:10

Vadzim


Stream does not implement Iterable, nor is it an array, so it is not eligible for use in an enhanced for loop. The reason it does not implement Iterable is because it is a single use data structure. Each time Iterable.iterator is called, a new Iterator should be returned which covers all of the elements. The iterator method on Stream just reflects the current state of the Stream. It is effectively a different view of the Stream.

You could make a class which implements Iterable by wrapping a stream for use in the enhanced for loop. But this is a questionable idea as you cannot truly implement Iterable (as described above).

like image 26
Brett Okken Avatar answered Oct 12 '22 02:10

Brett Okken


I wrote an extension to the Stream API which allows for checked exceptions to be thrown.

Stream<String> stream = Stream.of("1", "2", "3");
Writer writer = new FileWriter("example.txt");

ThrowingStream.of(stream, IOException.class).forEach(writer::append);
like image 2
Jeffrey Avatar answered Oct 12 '22 01:10

Jeffrey


You can also do it like this:

Path inputPath = Paths.get("...");
Stream<Path> stream = Files.list(inputPath);

for (Iterator<Path> iterator = stream.iterator(); iterator.hasNext(); )
{
    Path path = iterator.next();
    // ...
}
like image 2
BullyWiiPlaza Avatar answered Oct 12 '22 03:10

BullyWiiPlaza