509

I have an interface which returns java.lang.Iterable<T>.

I would like to manipulate that result using the Java 8 Stream API.

However Iterable can't "stream".

Any idea how to use the Iterable as a Stream without converting it to List?

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
rayman
  • 20,786
  • 45
  • 148
  • 246

8 Answers8

691

There's a much better answer than using spliteratorUnknownSize directly, which is both easier and gets a better result. Iterable has a spliterator() method, so you should just use that to get your spliterator. In the worst case, it's the same code (the default implementation uses spliteratorUnknownSize), but in the more common case, where your Iterable is already a collection, you'll get a better spliterator, and therefore better stream performance (maybe even good parallelism). It's also less code:

StreamSupport.stream(iterable.spliterator(), false)
             .filter(...)
             .moreStreamOps(...);

As you can see, getting a stream from an Iterable (see also this question) is not very painful.

Matthias Braun
  • 32,039
  • 22
  • 142
  • 171
Brian Goetz
  • 90,105
  • 23
  • 150
  • 161
  • 138
    A static method on `Stream` would have been nice, e.g. `Stream.ofIterable(iterable)`. – robinst Aug 27 '15 at 05:35
  • 87
    @robinst This wasn't an omission; it was a deliberate (and highly debated) choice. The challenge is that were this to exist, it would just be too easy to reach for it without understanding the tradeoffs that come with it. Because Iterable is so abstract, such a method would result in the worst-performing stream possible (no parallel support, no size information or characteristics (used to optimize execution choices)). Forcing more thought results in better APIs across the entire ecosystem. This was a tradeoff of "what's best for XYZ code" vs "what's best for all Java code." – Brian Goetz Aug 24 '16 at 15:03
  • 3
    Based on your explanation, I'm curious why we got Collection.stream() but not Iterable.stream(). It seems like the reasoning to omit Iterable.stream() (or Stream.ofIterable()) applies equally to Collection.stream(). – qualidafial Sep 04 '16 at 20:15
  • 6
    @qualidafial See http://stackoverflow.com/questions/23114015/why-does-iterablet-not-provide-stream-and-parallelstream-methods. – Brian Goetz Sep 04 '16 at 20:29
  • 14
    @BrianGoetz It looks most of people are not in the level to understand you said above or don't care about. they only want to write the simple code by calling simple API. On the other hand, those things(parallel...) maybe not important to most of daily iterable operations. – user_3380739 Dec 07 '16 at 00:27
  • 2
    Based on the explanations given so far, I would expect to have Stream.ofIterable(Iterable iterable, boolean parallel), and Stream.ofIterable(Iterable iterable), which defaults to a non-parallel stream of items. More generally speaking, StreamSupport helpers should be in the Stream class. – Stéphane Appercel Jul 15 '17 at 05:38
  • 1
    @StéphaneAppercel Please read the comment above between Brian and Robin. – Franklin Yu Dec 02 '18 at 04:39
94

If you can use Guava library, since version 21, you can use

Streams.stream(iterable)
numéro6
  • 3,921
  • 1
  • 19
  • 18
  • 3
    Or, if you are stuck at an older version, use `Lists.newArrayList(Iterable)`. – Jacob van Lingen Feb 03 '17 at 14:06
  • 1
    Current Guava implementation is not worse than accepted answer: https://github.com/google/guava/blob/master/guava/src/com/google/common/collect/Streams.java#L60 – Vadzim Jul 10 '19 at 11:17
24

You can easily create a Stream out of an Iterable or Iterator:

public static <T> Stream<T> stream(Iterable<T> iterable) {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            iterable.iterator(),
            Spliterator.ORDERED
        ),
        false
    );
}
nosid
  • 48,932
  • 13
  • 112
  • 139
  • 2
    You have to write this function once and then just call it. Why does a call to `stream(...)` clutter your code? – gexicide May 29 '14 at 11:42
  • 1
    Wanted to do it Inline short and elegant.. you right I can write this function once.. but I am into dropping code (and not adding code). anyway this answer is right because thats the way to convert this. – rayman May 29 '14 at 11:45
  • static import said function. short and elegant. (though not necessarily transparent) – aepurniet May 29 '14 at 11:49
11

I would like to suggest using JOOL library, it hides spliterator magic behind the Seq.seq(iterable) call and also provides a whole bunch of additional useful functionality.

Saikat
  • 14,222
  • 20
  • 104
  • 125
Shaggie
  • 15,034
  • 1
  • 15
  • 8
8

So as another answer mentioned Guava has support for this by using:

Streams.stream(iterable);

I want to highlight that the implementation does something slightly different than other answers suggested. If the Iterable is of type Collection they cast it.

public static <T> Stream<T> stream(Iterable<T> iterable) {
  return (iterable instanceof Collection)
    ? ((Collection<T>) iterable).stream()
    : StreamSupport.stream(iterable.spliterator(), false);
}

public static <T> Stream<T> stream(Iterator<T> iterator) {
  return StreamSupport.stream(
    Spliterators.spliteratorUnknownSize(iterator, 0),
    false
  );
}
Alexander Oh
  • 24,223
  • 14
  • 73
  • 76
6

I've created this class:

public class Streams {
    /**
     * Converts Iterable to stream
     */
    public static <T> Stream<T>  streamOf(final Iterable<T> iterable) {
        return toStream(iterable, false);
    }

    /**
     * Converts Iterable to parallel stream
     */
    public static <T> Stream<T> parallelStreamOf(final Iterable<T> iterable) {
        return toStream(iterable, true);
    }

    private static <T> Stream<T> toStream(final Iterable<T> iterable, final boolean isParallel) {
        return StreamSupport.stream(iterable.spliterator(), isParallel);
    }
}

I think it's perfectly readable because you don't have to think about spliterators and booleans (isParallel).

g-t
  • 1,455
  • 12
  • 17
4

A very simple work-around for this issue is to create a Streamable<T> interface extending Iterable<T> that holds a default <T> stream() method.

interface Streamable<T> extends Iterable<T> {
    default Stream<T> stream() {
        return StreamSupport.stream(spliterator(), false);
    }
}

Now any of your Iterable<T>s can be trivially made streamable just by declaring them implements Streamable<T> instead of Iterable<T>.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
0

If you happen to use Vavr(formerly known as Javaslang), this can be as easy as:

Iterable i = //...
Stream.ofAll(i);
Grzegorz Piwowarek
  • 13,172
  • 8
  • 62
  • 93