16

In order to keep implementation details from leaking, instead of returning, e.g., a Collection<MyCoolObject>, one might implement Iterable<MyCoolObject>, which would then require implementing Iterator<T> from the Iterable Interface. Thus however the internal data structure is managed, the access to the elements is via the Iterator.

With Java 8, one might wish to add Stream<MyCoolObject> stream() to MyCoolObject. (See also: recommendation to support stream in the book Java 8 Lambdas). While adding the method isn't difficult (and I did read the Question about Why Iterable Doesn't Provide Stream), it seems odd that Java did not add an Interface for Streamable<T> to mirror the Iterable<T> idea. (Well, a different name perhaps since Streamable exists for the ever use CORBA stuff).

I think I followed the Answer about why adding Stream to Iterable was potentially problematic, but I do not see why a Streaming<T> interface couldn't have been provided. For example, Collections could have implemented the Streaming<T> interface, and it would make it clearer for other objects that one could expect a stream() method.

Based upon an Answer to the above referenced Question, it is possible to get a Stream from the Iterable via

Stream s = StreamSupport.stream(iter.spliterator(), false);

but that seems like a lot of work given that MyObject could would like to just implement stream() to allow a user of the object to do

myObject.stream().filter(...).collect(...)

without the intervening conversion from the iterator.

Is there a reason for the lack of an interface for streaming capable objects? Is there a better approach than just implementing stream() on MyCoolObject and letting someone look through the Javadoc so they know it has a stream() method?

Or, is quite likely, am I misunderstanding something about the approach for Stream?

(Also, I implement stream() in CoolObject, but then forget to implement parallelStream(), which is something that would be alleviated by having the interface).

Community
  • 1
  • 1
KevinO
  • 4,303
  • 4
  • 27
  • 36
  • 4
    How does implementing an interface make it any clearer than just providing the method? You have to look at the Javadoc either way. (And why bother with `parallelStream()` when `stream().parallel()` does the same thing?) You haven't made it particularly clear what you expect the _interface_ to add versus just adding a `stream()` method directly. – Louis Wasserman May 10 '17 at 20:07
  • 1
    You don't need a `Streaming` interface to provide a `stream` method. `Iterable` is there to support for-each loops. Is there anything like that for streams, where an API needs to take a generic stream-producing object instead of just taking a stream? – user2357112 May 10 '17 at 20:11
  • 1
    @LouisWasserman for abstraction when you return an object? So that you can say: "here take this object, from which you can obtain a Stream, but you don't need to know exactly what type of object it is" – njzk2 May 10 '17 at 20:13
  • @LouisWasserman, fair enough about the Javadoc. But if `Iterable` exists "to be the target of a `for-each loop`" (from its Javadoc), then why not an interface to be the target of a `stream`? I am guessing consistency as to the reason. I create MyObject, and implement `Iterable`, but not `Streaming`? Just seems like it is missing something. – KevinO May 10 '17 at 20:13
  • (Note that before Java 1.5 introduced for-each loops, `Iterable` didn't exist. Similarly, there was no `Enumerable` for the old pre-Java Collections Framework `elements`/`Enumeration` API.) – user2357112 May 10 '17 at 20:14
  • @user2357112, here might be a use case issue on my part. But I am creating an `interface` for essentially a Strategy pattern. Rather than specifying a method return `Collection`, I thought hey, don't force the Strategy Implementor to make such a specific return, instead just allow supporting iteration and streaming (as that is what is really needed). I can have my interface extend `Iterable` but then I must directly specify `Stream stream` on the interface? Just seems strange, but your comment about when Iterable was introduced was enlightening. Thank you. – KevinO May 10 '17 at 20:25
  • @KevinO Why have you made the assumption that a collection is more specific than a stream? It's the other way around. If you return a stream you'd be returning something that is simultaneously more specific *and* less useful. – Michael May 10 '17 at 20:53
  • 4
    @KevinO A strategy that supplies some value is often better described by a functional interface, e.g. a Supplier. A stream provides an `iterator()` method to get an iterator (only one time), so why not declare the strategy as Supplier>? – Jens May 10 '17 at 21:07
  • Possible duplicate of [What happened to java.util.stream.Streamable?](http://stackoverflow.com/questions/21985854/what-happened-to-java-util-stream-streamable) – Didier L May 11 '17 at 08:05
  • 1
    I wouldn’t object a name, just because there is a class of the same simple name in CORBA. There are so many classes in there, it would be hard to find a name which isn’t there. Just start with `Object`… – Holger May 11 '17 at 10:11

2 Answers2

11

This should probably augment any future answers.

I don't know why you think that returning an Iterable<MyCoolObject> rather than a Collection<MyCoolObject> is better. It might hide details indeed, and will create more problems as-well.

A Collection has a known size that plays a big role while splitting for parallel processing. This is reported as Spliterator.SIZED | Spliterator.SUBSIZED. So a Collection.stream will handle parallel streams much better then a Iterable, that will use:

public static <T> Spliterator<T> spliteratorUnknownSize 

that is documented as:

... and implements trySplit to permit limited parallelism.

Which is obvious, since you don't know the size at all. Under the current implementation the batch size is 1024. So, for example, for anything under 1024 elements you would not get any parallelisation at all.

Now as far as your question goes, there used to be such a thing in the early builds of jdk-8. It was called java.util.stream.Streamable. From what I know it was removed because there are methods that return a Stream, but not via the stream() method.

String::codePoints()
File::lines
Pattern::splitAsStream
... many others

So the only place where this would be implemented would be the Collections. And that as far as I can tell this would be a really isolated place.

Aha moment

Here is the explanation from the people in charge of this.

As suggested here are the reasons for the removal:

I am considering dropping the Streamable interface. Currently the only implementor is Collection, and all of the other stream-bearing methods are serving up specialized streams (chars(), codePoints(), lines(), etc) with a method name that is more suitable than "stream". So I think we should drop Streamable and leave the stream() / parallel() methods on Collection (or possibly move them up Iterable).

Eugene
  • 117,005
  • 15
  • 201
  • 306
  • "It might hide details indeed..." Except it does this for the wrong reason. It makes assumptions about how it's going to be called. No function should ever be that arrogant. You want to return a type that is generic enough but also widely useful. Returning `Iterable` is not widely useful, hence why there are hardly any classes which *only* implement `Iterable` and do not implement `Collection` – Michael May 10 '17 at 20:59
  • 2
    I wish there was an interface for `isEmpty()` that things with that method have, but there isn't. Just like there's no `Streamable` interface. It's too late - that ship has sailed. – Bohemian May 10 '17 at 21:04
  • Thank you for the great response. The "Aha moment" was really interesting. – KevinO May 10 '17 at 21:22
  • 3
    Good sleuthing to dig up Brian Goetz' comment. I'd suggest you pull in the key sentences from that email in addition to providing a link to the mail archive. – Stuart Marks May 10 '17 at 23:03
1

Because it was removed, but you can easily roll out your own Streamable implementation if you need it:

@FunctionalInterface
interface Streamable<T> {
    Stream<T> stream();
}

If we take the example of the strategy pattern, you would then define it like so:

interface StreamStrategy<T> {
    Streamable<T> getStreamable();
}

Which can easily be implemented based on any backing object providing a method returning a Stream<T>, using a method reference. For example, if you have a collection:

class CollectionBasedStrategy<T> implements StreamStrategy<T> {
    @Override
    public Streamable<T> getStreamable() {
        return new ArrayList<T>()::stream;
    }
}

If Collection was extending such a Streamable interface, you would indeed not need to use the method reference. But otherwise there didn't seem to be much added value to put that in the JDK – and it can still be added later if needed.

Community
  • 1
  • 1
Didier L
  • 18,905
  • 10
  • 61
  • 103