306

In Java 8 we have the class Stream<T>, which curiously have a method

Iterator<T> iterator()

So you would expect it to implement interface Iterable<T>, which requires exactly this method, but that's not the case.

When I want to iterate over a Stream using a foreach loop, I have to do something like

public static Iterable<T> getIterable(Stream<T> s) {
    return new Iterable<T> {
        @Override
        public Iterator<T> iterator() {
            return s.iterator();
        }
    };
}

for (T element : getIterable(s)) { ... }

Am I missing something here?

Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
roim
  • 4,780
  • 2
  • 27
  • 35
  • 8
    not to mention that the other 2 methods of iterable (forEach and spliterator) are also in Stream – njzk2 Nov 21 '13 at 19:09
  • 1
    this is needed to pass `Stream` to legacy APIs that expects `Iterable` – ZhongYu Nov 21 '13 at 19:52
  • 14
    A good IDE (e.g. IntelliJ) will prompt you to simplify your code in `getIterable()` to `return s::iterator;` – ZhongYu Nov 21 '13 at 19:54
  • 25
    You don't need a method at all. Where you have a Stream, and want an Iterable, just pass stream::iterator (or, if you prefer, () -> stream.iterator()), and you're done. – Brian Goetz Apr 20 '14 at 17:34
  • You are not missing anything, and by using for(T t: iterable), you are also avoiding the necessity to make all the exceptions unchecked. – The Coordinator Mar 28 '15 at 19:08
  • 8
    Unfortunately I cannot write `for (T element : stream::iterator)`, so I'd still prefer if Stream would also implement `Iterable` or a method `toIterable()`. – Thorsten May 15 '15 at 05:34
  • Trying to process a `Stream` as an `Iterator` is just the wrong usage pattern. As an example, you can choose to `Stream` a `List` using `list.stream()`, or iterate a list using `for (E e:list) {...}`. You choose a stream with possible parallelization and out of order processing in mind, or for the possibility to easily chain multiple operations. As most usage originates from a `Collection` anyhow, and you can directly iterate Collections already using the shorthand `for` operator, it seems an anti pattern to allow the same for a stream. – YoYo May 23 '15 at 15:44
  • 1
    @JoD besides their ability to do parallelism, streams are supposed to be syntactic sugar for doing operations on collections. In c#, they're called `Language-Integrated Query` and work well. Unfortunately, in Java they fail at that, which is why you might not think of them in that way. Thankfully, there's `StreamEx` which is both parallel and can actually make your code nicer. – Aleksandr Dubinsky May 30 '15 at 15:32
  • @AleksandrDubinsky I am seeing so many references to StreamEx that I am ready to cave in right now. I like 'pure code' that is able to avoid use of external libraries without giving in to flexibility. Hope it has all missing Scala sugar. – YoYo Jun 14 '15 at 19:06
  • Possible duplicate of [Why are Java Streams once-off?](http://stackoverflow.com/questions/28459498/why-are-java-streams-once-off) – Raedwald Dec 21 '15 at 18:43
  • @Raedwald considering this question is 14 months older, I'd say it's the other way around if anything – roim Dec 22 '15 at 16:04
  • @Raedwald and in fact they're very different questions – roim Dec 22 '15 at 16:08
  • 1
    I gave you your first gold badge, by up-voting your question from +99 to +100 :). I found it by looking at all the questions with 99 votes, and those who posted them without a gold badge ;). – Luke Feb 19 '16 at 06:56

9 Answers9

228

People have already asked the same on the mailing list ☺. The main reason is Iterable also has a re-iterable semantic, while Stream is not.

I think the main reason is that Iterable implies reusability, whereas Stream is something that can only be used once — more like an Iterator.

If Stream extended Iterable then existing code might be surprised when it receives an Iterable that throws an Exception the second time they do for (element : iterable).

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • 31
    Curiously there were already some iterables with this behavior in Java 7, e.g. DirectoryStream: While DirectoryStream extends Iterable, it is not a general-purpose Iterable as it supports only a single Iterator; invoking the iterator method to obtain a second or subsequent iterator throws IllegalStateException. (http://openjdk.java.net/projects/nio/javadoc/java/nio/file/DirectoryStream.html) – roim Nov 21 '13 at 20:01
  • @roim And if DirectoryStream is designed in Java 8 it would very likely extend Stream instead of Iterable, as the name suggested. – kennytm Nov 21 '13 at 20:19
  • 41
    Unfortunately there's nothing is the documentation of `Iterable` about whether `iterator` should always or might not be callable multiple times. That's something they should put in there. This seems to be more of a standard practice than a formal specification. – Lii Apr 02 '14 at 09:06
  • 7
    If they're going to use excuses, you'd think they could at least add an asIterable() method - or overloads for all those methods which only take Iterable. – Hakanai Apr 11 '14 at 23:24
  • 33
    Maybe the best solution would have been making Java's foreach accept an Iterable as well as potentially a Stream? – devoured elysium Apr 22 '14 at 23:49
  • 3
    @Lii As standard practices go, it's a pretty strong one though. – biziclop May 28 '15 at 15:22
  • This just bit me with AssertJ - if you assertThat(...an Iterable).containsOnly(...an Iterable) and you have converted two streams to iterable then AssertJ will iterate the assertThat argument more than once but the containsOnly argument only once. This is of course implementation dependent, and could change with the next version of AssertJ. (Not to pick on AssertJ at all, it's just where I discovered this.) – davidbak Jan 04 '16 at 17:58
  • 2
    @devouredelysium IMO it should accept Iterable and Iterator – user253751 Apr 21 '17 at 08:31
  • "... then existing code might be surprised when it receives an `Iterable` that throws an `Exception` ..." - they shoulda just thrown the exception IMHO – gzak Oct 09 '18 at 00:14
  • 4
    [JDK-8148917](https://bugs.openjdk.java.net/browse/JDK-8148917?focusedCommentId=14248575&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-14248575) contains a proposal to make streams `IterableOnce` which might solve this in a future Java release. – Nathan Williams Apr 06 '19 at 18:38
  • 2
    More evidence that Java has just become a poorly assembled kludge. There are so many half-baked features in an attempt to compete with more modern languages, and all of them fall short. – Josh M. Mar 03 '20 at 02:40
  • 1
    additionally behavior of `forEach(...)` is by default different: for an `Iterable`, the passed `Consumer` is by default executed sequentially, while for a `Stream` it may be executed by any thread that implementation chooses (even for sequential streams). Although `Iterable.forEach(...)`'s javadoc states _"Unless otherwise specified by the implementing class"_, it also has become a strong convention. Finally any `RuntimeException` or `Error` thrown by a `Consumer` passed to `Iterable.forEach()` **must** be relayed to the caller, while no such requirement for `Stream`. – morgwai Dec 02 '21 at 05:57
178

To convert a Stream to an Iterable, you can do

Stream<X> stream = null;
Iterable<X> iterable = stream::iterator

To pass a Stream to a method that expects Iterable,

void foo(Iterable<X> iterable)

simply

foo(stream::iterator) 

however it probably looks funny; it might be better to be a little bit more explicit

foo( (Iterable<X>)stream::iterator );
ZhongYu
  • 19,446
  • 5
  • 33
  • 61
  • 77
    You can also use this in a loop `for(X x : (Iterable)stream::iterator)`, although it looks ugly. Really, the whole situation is just absurd. – Aleksandr Dubinsky Nov 26 '13 at 14:59
  • 4
    In Scala I can do: `(0 to N) foreach println` The same thing in Java 8 is: `for (int i: (Iterable) IntStream.range(0, N)::iterator) { System.out.println(i) }` – HRJ Jun 29 '14 at 03:43
  • 24
    @HRJ `IntStream.range(0,N).forEach(System.out::println)` – MikeFHay Jul 24 '14 at 15:32
  • @MikeFHay lambdas in Java are nice but don't play well with other features of the language. For example, dealing with checked exceptions and unit types is a pain. – HRJ Jul 25 '14 at 03:56
  • 2
    @HRJ hopefully they'll fix them up in java9. hopefully. – ZhongYu Jul 27 '14 at 15:13
  • 13
    I don't understand the double colon syntax in this context. What's the difference between `stream::iterator` and `stream.iterator()`, that makes the former acceptable for an `Iterable` but not the latter? – Daniel C. Sobral Dec 19 '14 at 03:28
  • 22
    Answering myself: `Iterable` is a functional interface, so passing a function that implements it is enough. – Daniel C. Sobral Dec 19 '14 at 03:30
  • 8
    I have to agree with @AleksandrDubinsky , The real problem is that there is a symantic workaround for(X x : (Iterable)stream::iterator), which just allows the same operation albeit at a great code-noise. But then this is Java and we have tolerated the List list = new ArrayList(Arrays.asList(array)) for a long time :) Although the powers that be could just offer us all List list = array.toArrayList(). But the real absurdity, is that by encouraging everyone to use forEach on Stream, **the exceptions must necessarily be unchecked** [rant ended]. – The Coordinator Mar 28 '15 at 19:07
  • @SaintHill You can read my rant about `forEach` [here](http://stackoverflow.com/a/20177092/1151521) – Aleksandr Dubinsky Mar 31 '15 at 18:29
  • @MikeFHay Your example doesn't print the Integers in order like the Scala example. To do that, you have to use forEachOrdered. – wegry May 23 '15 at 17:02
  • 2
    @wegry AFAIK `forEachOrdered()` is only required if the stream itself is parallel, which it isn't in this case. – biziclop May 28 '15 at 15:27
  • 6
    It's worth noting that this will break if the receiving code attempts to re-use the Iterable (two separate for-each loops, for example), per [kennytm's answer](http://stackoverflow.com/a/20130131/1188377). This technically breaks spec. You can only use a stream once; you can't call BaseStream::iterator twice. The first call terminates the stream. Per the JavaDoc: "This is a terminal operation." While this workaround if convenient, you shouldn't pass the resulting Iterable to code that isn't under your ultimate control. – Zenexer Jan 22 '16 at 08:00
  • 2
    The iterable reuse issue only exists because the method reference captures the stream instance. You can avoid the issue by generating the stream in a lambda: `Iterable iterable = () -> source.stream().iterator();` – shmosel Nov 08 '16 at 02:59
  • @AleksandrDubinsky, [`Iterable`](https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html) isn't a [`FunctionalInterface`](https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html). See, for example, [Iterable source code for OpenJDK 8](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/Iterable.java). Any ideas? – Sasha Dec 08 '16 at 18:02
  • 1
    @Sasha Sure it is. It has exactly one abstract method, so it is a functional interface according to the Java language specification. – Stefan Zobel Dec 08 '16 at 20:31
  • @StefanZobel, ah, sorry… I thought functional interface is those who has [`@FunctionalInterface`](https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html) annotation. I missed, that the annotation only declares the intention, not the fact. – Sasha Dec 09 '16 at 07:31
  • 1
    Ok. Really confused. Just, so confused. Because: doesn't this not work? An Iterator is not Iterable... Am I missing something? Doesn't this fail to compile? Right? Am I crazy? – Benjamin Dec 16 '16 at 23:24
  • 1
    @Ben - an Iterable is a functional interface with a single abstract method `interface Iterable{ Iterator iterator(); }`. To create an Iterable with lambda expression, we do `()->{ return some iterator }`. In this case, `()->stream.iterator()`; and with method reference, `stream::iterator` – ZhongYu Dec 17 '16 at 14:46
  • 2
    @Ben - Remember, `::iterator` is not an iterator, it is a function that returns an iterator. – ZhongYu Dec 17 '16 at 14:52
  • @ZhongYu technically, `stream::iterator` is not a function, it is an instance of `Iterable` *with* a function. Just wanted to point out that as always, there are no first-class functions in Java. – Andy Jun 08 '17 at 04:33
11

I would like to point out that StreamEx does implement Iterable (and Stream), as well as a host of other immensely awesome functionality missing from Stream.

Aleksandr Dubinsky
  • 22,436
  • 15
  • 82
  • 99
11

You can use a Stream in a for loop as follows:

Stream<T> stream = ...;

for (T x : (Iterable<T>) stream::iterator) {
    ...
}

(Run this snippet here)

(This uses a Java 8 functional interface cast.)

(This is covered in some of the comments above (e.g. Aleksandr Dubinsky), but I wanted to pull it out into an answer to make it more visible.)

Rich
  • 15,048
  • 2
  • 66
  • 119
  • 4
    Almost everyone needs to look at it twice or thrice before they realize how it even compiles. It is absurd. (This is covered in the response to the comments in the same comment stream, but I wanted to add the comment here to make it more visible.) – T Tse Jun 08 '20 at 10:36
7

kennytm described why it's unsafe to treat a Stream as an Iterable, and Zhong Yu offered a workaround that permits using a Stream as in Iterable, albeit in an unsafe manner. It's possible to get the best of both worlds: a reusable Iterable from a Stream that meets all the guarantees made by the Iterable specification.

Note: SomeType is not a type parameter here--you need to replace it with a proper type (e.g., String) or resort to reflection

Stream<SomeType> stream = ...;
Iterable<SomeType> iterable = stream.collect(toList()):

There is one major disadvantage:

The benefits of lazy iteration will be lost. If you planned to immediately iterate over all values in the current thread, any overhead will be negligible. However, if you planned to iterate only partially or in a different thread, this immediate and complete iteration could have unintended consequences.

The big advantage, of course, is that you can reuse the Iterable, whereas (Iterable<SomeType>) stream::iterator would only permit a single use. If the receiving code will be iterating over the collection multiple times, this is not only necessary, but likely beneficial to performance.

Lii
  • 11,553
  • 8
  • 64
  • 88
Zenexer
  • 18,788
  • 9
  • 71
  • 77
  • 1
    Have you tried to compile your code before answering? It doesn't work. – Tagir Valeev Jan 22 '16 at 08:48
  • 1
    @TagirValeev Yes, I did. You need to replace T with the appropriate type. I copied the example from working code. – Zenexer Jan 23 '16 at 07:02
  • 2
    @TagirValeev I tested it again in IntelliJ. It appears that IntelliJ sometimes gets confused by this syntax; I haven't really found a pattern to it. However, the code compiles fine, and IntelliJ removes the error notice after compiling. I guess it's just a bug. – Zenexer Jan 23 '16 at 07:31
  • I've replaced `T` with `SomeType` to clarify that it's not a type parameter. – Zenexer Aug 30 '16 at 23:22
  • 1
    `Stream.toArray()` returns an array, not an `Iterable`, so this code still doesn't compile. but it may be a bug in eclipse, since IntelliJ seems to compile it – benez Dec 05 '16 at 00:08
  • 1
    @Zenexer How were you able to assign an array to an Iterable? – radiantRazor Apr 16 '17 at 20:03
  • @radiantRazor Looks like I copied it incorrectly; I'm missing a step. However, it's probably just easier to collect it as a list. There was a reason I did it this way, but I can't remember what it was; it handled some edge case. I'll try to figure out what I was thinking when I get home. – Zenexer Apr 16 '17 at 20:08
  • I think I was aiming for this: http://stackoverflow.com/a/41517893/1188377 – Zenexer Apr 16 '17 at 20:09
3

Stream does not implement Iterable. The general understanding of Iterable is anything that can be iterated upon, often again and again. Stream may not be replayable.

The only workaround that I can think of, where an iterable based on a stream is replayable too, is to re-create the stream. I am using a Supplier below to create a new instance of stream, everytime a new iterator is created.

    Supplier<Stream<Integer>> streamSupplier = () -> Stream.of(10);
    Iterable<Integer> iterable = () -> streamSupplier.get().iterator();
    for(int i : iterable) {
        System.out.println(i);
    }
    // Can iterate again
    for(int i : iterable) {
        System.out.println(i);
    }
Ashish Tyagi
  • 191
  • 3
  • 16
2

If you don't mind using third party libraries cyclops-react defines a Stream that implements both Stream and Iterable and is replayable too (solving the problem kennytm described).

 Stream<String> stream = ReactiveSeq.of("hello","world")
                                    .map(s->"prefix-"+s);

or :-

 Iterable<String> stream = ReactiveSeq.of("hello","world")
                                      .map(s->"prefix-"+s);

 stream.forEach(System.out::println);
 stream.forEach(System.out::println);

[Disclosure I am the lead developer of cyclops-react]

Community
  • 1
  • 1
John McClean
  • 5,225
  • 1
  • 22
  • 30
0

Not perfect, but will work:

iterable = stream.collect(Collectors.toList());

Not perfect because it will fetch all items from the stream and put them into that List, which is not exactly what Iterable and Stream are about. They are supposed to be lazy.

yegor256
  • 102,010
  • 123
  • 446
  • 597
-1

You can iterate over all files in a folder using Stream<Path> like this:

Path path = Paths.get("...");
Stream<Path> files = Files.list(path);

for (Iterator<Path> it = files.iterator(); it.hasNext(); )
{
    Object file = it.next();

    // ...
}
BullyWiiPlaza
  • 17,329
  • 10
  • 113
  • 185