How to iterate with foreach loop over java 8 stream

14,734

Solution 1

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

Solution 2

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

Solution 3

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();
    // ...
}

Solution 4

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).

Share:
14,734

Related videos on Youtube

Vadzim
Author by

Vadzim

Vadzim Kapichenka aka Vadim Kopichenko

Updated on July 26, 2022

Comments

  • Vadzim
    Vadzim almost 2 years

    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.

    • blgt
      blgt almost 9 years
    • Vadzim
      Vadzim almost 9 years
      @blgt, no that link doesn't give the answer
    • blgt
      blgt almost 9 years
      It does, for the question as stated. After reading your answer, it might be closer to a duplicate of Why does Stream<T> not implement Iterable<T>? but you might want to specify these details and highlight differences more explicitly
    • Vadzim
      Vadzim almost 9 years
      @blgt, it seems to me that "howto" question can't be rated as complete duplicate of "why doesn't" question just because they require different answers. Anyway, mentioned question doesn't come up when searching for this question's subject because there is no exlicit mention of foreach loop there.
    • Vadzim
      Vadzim almost 9 years
      @blgt, I've updated the question as you insist. Btw, it seems to me that iteration over stream with checked exceptions involved is much more practical problem for most developers (including me) than abstract Stream to Iterable conversion.
    • Basil Bourque
      Basil Bourque about 3 years
  • Brett Okken
    Brett Okken almost 9 years
    small nit: The enhanced for loop (foreach) also works with arrays, which do not implement Iterable.
  • Vadzim
    Vadzim almost 9 years
    @Brett, of cource, this was omitted as unrelated to this case detail
  • Vadzim
    Vadzim almost 9 years
    I've firstly found this trick here. Much later in more detail here. Hope this question would top in peoples searches unlike these links.
  • Marko Topolnik
    Marko Topolnik almost 9 years
    A more general hack is available which works for arbitrary higher-order functions, not just forEach.