Really Too Bad that Java 8 Doesn’t Have Iterable.stream()
This is one of the more interesting recent Stack Overflow questions:
Why does Iterable not provide stream() and parallelStream() methods?
At first, it might seem intuitive to make it straight-forward to convert an Iterable
into a Stream
, because the two are really more or less the same thing for 90% of all use-cases.
Granted, the expert group had a strong focus on making the Stream
API parallel capable, but anyone who works with Java every day will notice immediately, that Stream
is most useful in its sequential form. And an Iterable
is just that. A sequential stream with no guarantees with respect to parallelisation. So, it would only be intuitive if we could simply write:
iterable.stream();
In fact, subtypes of Iterable
do have such methods, e.g.
collection.stream();
Brian Goetz himself gave an answer to the above Stack Overflow question. The reasons for this omittance are rooted in the fact that some Iterables
might prefer to return an IntStream
instead of a Stream
. This really seems to be a very remote reason for a design decision, but as always, omittance today doesn’t mean omittance forever. On the other hand, if they had introduced Iterable.stream()
today, and it turned out to be a mistake, they couldn’t have removed it again.
Well, primitive types in Java are a pain and they did all sorts of bad things to generics in the first place, and now to Stream
as well, as we have to write the following, in order to turn an Iterable
into a Stream
:
Stream s = StreamSupport.stream(iterable.spliterator(), false);
Brian Goetz argues that this is “easy”, but I would disagree. As an API consumer, I experience a lot of friction in productivity because of:
- Having to remember this otherwise useless
StreamSupport
type. This method could very well have been put into theStream
interface, because we already haveStream
construction methods in there, such asStream.of()
. - Having to remember the subtle difference between
Iterator
andSpliterator
in the context of what I believe has nothing to do with parallelisation. It may well be thatSpliterators
will become popular eventually, though, so this doubt is for the magic 8 ball to address. - In fact, I have to repeat the information that there is nothing to be parallelised via the boolean argument
false
Parallelisation really has such a big weight in this new API, even if it will cover only around 5%-10% of all functional collection manipulation operations. While sequential processing was not the main design goal of the JDK 8 APIs, it is really the main benefit for all of us, and the friction around APIs related to sequential processing should be as low as possible.
The method above should have just been called:
Stream s = Stream.stream(iterable);
It could be implemented like this:
public static<T> Stream<T> stream(Iterable<T> i) { return StreamSupport.stream(i.spliterator(), false); }
Obviously with convenience overloads that allow for the additional specialisations, like parallelisation, or passing a Spliterator
But again, if Iterable
had its own stream()
default method, an incredible number of APIs would be so much better integrated with Java 8 out of the box, without even supporting Java 8 explicitly!
Take jOOQ for instance. jOOQ still supports Java 6, so a direct dependency is not possible. However, jOOQ’s ResultQuery
type is an Iterable
. This allows you to use such queries directly inline in foreach loops, as if you were writing PL/SQL:
PL/SQL
FOR book IN ( SELECT * FROM books ORDER BY books.title ) LOOP -- Do things with book END LOOP;
Java
for (BookRecord book : ctx.selectFrom(BOOKS).orderBy(BOOKS.TITLE) ) { // Do things with book }
Now imagine the same thing in Java 8:
ctx.selectFrom(BOOKS).orderBy(BOOKS.TITLE) .stream() .map / reduce / findAny, etc...
Unfortunately, the above is currently not possible. You could, of course, eagerly fetch all the results into a jOOQ Result
, which extends List
:
ctx.selectFrom(BOOKS).orderBy(BOOKS.TITLE) .fetch() .stream() .map / reduce / findAny, etc...
But it’s one more method to call (every time), and the actual stream semantics is broken, because the fetch is done eagerly.
Complaining on a high level
This is, of course, complaining on a high level, but it would really be great if a future version of Java, e.g. Java 9, would add this missing method to the Iterable
API. Again, 99% of all use-cases will want the Stream
type to be returned, not the IntStream
type. And if they do want that for whatever obscure reason (much more obscure than many evil things from old legacy Java APIs, looking at you Calendar
), then why shouldn’t they just declare an intStream()
method. After all, if someone is crazy enough to write Iterable<Integer>
when they’re really operating on int
primitive types, they’ll probably accept a little workaround.
Reference: | Really Too Bad that Java 8 Doesn’t Have Iterable.stream() from our JCG partner Lukas Eder at the JAVA, SQL, AND JOOQ blog. |
What about adding ResultQuery.stream() ?