Why Does Stream<T> Not Implement Iterable<T>

Why does StreamT not implement IterableT?

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

Why does IterableT not provide stream() and parallelStream() methods?

This was not an omission; there was detailed discussion on the EG list in June of 2013.

The definitive discussion of the Expert Group is rooted at this thread.

While it seemed "obvious" (even to the Expert Group, initially) that stream() seemed to make sense on Iterable, the fact that Iterable was so general became a problem, because the obvious signature:

Stream<T> stream()

was not always what you were going to want. Some things that were Iterable<Integer> would rather have their stream method return an IntStream, for example. But putting the stream() method this high up in the hierarchy would make that impossible. So instead, we made it really easy to make a Stream from an Iterable, by providing a spliterator() method. The implementation of stream() in Collection is just:

default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}

Any client can get the stream they want from an Iterable with:

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

In the end we concluded that adding stream() to Iterable would be a mistake.

Make a Stream into an Iterable?

As explained in Why does Stream<T> not implement Iterable<T>?, an Iterable bears the expectation to be able to provide an Iterator more than once, which a Stream can’t fulfill. So while you can create an Iterable out of a Stream for an ad-hoc use, you have to be careful about whether attempts to iterate it multiple times could exist.

Since you said, “I need to pass those parts of string as an Iterable to a specific library”, there is no general solution as the code using the Iterable is outside your control.

But if you are the one who creates the stream, it is possible to create a valid Iterable which will simply repeat the stream construction every time an Iterator is requested:

Iterable<String> lines = () -> "this\nthat\nthe_other".lines().iterator();

This fulfills the expectation of supporting an arbitrary number of iterations, while not consuming more resources than a single stream when being traversed only once.

for(var s: lines) System.out.println(s);
lines.forEach(System.out::println);
System.out.println(String.join("\n", lines));

why is it possible that the stream::iterator returns to Iterable in java?

Iterable<E> is a functional interface. That means that any lambda meeting the criteria of its sole method, Iterable<E> iterator(), can act as an implementation of this interface.

That means that any lambda that takes no parameters and returns an Iterator<E> can be used as an instance of Iterable<E>.

Now, the notation stream::iterator is syntactic sugar for the lambda () -> stream.iterator(), which is a lambda that meets the above criteria. stream::iterator is thus a valid return value for a method that returns Iterable<E>.

Why do objects need to implement IterableT to be the target of for-each loops, if it requires no implementation?

The Iterable require the class to implement the method Iterator iterator().

It is not an empty interface.
See: https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html

Why is Java's Iterator not an Iterable?

Because an iterator generally points to a single instance in a collection. Iterable implies that one may obtain an iterator from an object to traverse over its elements - and there's no need to iterate over a single instance, which is what an iterator represents.

Converting an AutoCloseable, Iterable class into a Stream

As stated in the javadoc, BaseStream.onClose() "Returns an equivalent stream with an additional close handler":

public class WorkingExample {
public static void main(final String[] args) {
MyCursor cursor = new MyCursor();
try (Stream<String> stream = StreamSupport.stream(cursor.spliterator(), false)
.onClose(cursor::close)) {
stream.forEach(System.out::println);
}
}
}

will call MyCursor.close() as desired.

why a function like this idGetter can not be applied to Map function in java stream api?

Function<? extends T, Long> means:

Any function that converts some specific but unknown thing to a Long, where we do know that, whatever it is, it's either T or some subtype thereof.

It specifically does not mean: "A function that can take any T and convert it to a Long". If you want that, you'd need a Function<T, Long>.

List<? extends T> means:

A list whose elements are constrained; they must all be instanceof some type we don't know. We do know that, whatever type it is, it is at least T or some subtype of T.

It specifically does not mean: A list whose elements are constrained to be instanceof T. Because that'd just be a List<T>.

The expression list.get(0), if list is a List<T>, is itself T. Obviously. It's still T if the list is a List<? extends T> instead. The crucial difference is with add: you can call list.add(someInstanceOfT) just fine, if the type of list is List<T>. However, list.add(anything) doesn't compile if list's type is List<? extends T>.

Think about it:

List<Integer> myList = new ArrayList<Integer>();
List<? extends Number> numbers = myList;
numbers.add(someDouble);

The above does not compile - specifically, the third line (numbers.add) does not. And now it should be obvious why: The numbers variable is pointing at a list whose constraint is unknown. Maybe it is Integer, which would mean adding a double to it, is broken.

The key thing to keep in mind is PECS: Produce-Extends-Consume-Super. This is from the point of view of the object: a list.get() call "produces" a value, therefore, extends bounds are useful (a List<? extends Number> can be used for meaningful production, because PE: Produce-Extends: .get() call, which produces, gives you Number. Or flip it around, CS: a List<? super Number> can meaningfully consume: list.add(someNumber) is allowed on them. But list.get(0) on a List<? super Number> is not particularly useful (it gets you an object, because all lists can't store anything else, but nothing more specific).

Now back to functions. The first typearg of a function is solely used for consumption: The point of that first arg is that your function consumes these. The second argument is solely used for production.

Thinking about PECS, Function<? extends Anything, _> is completely useless, given that extends means its useful only for production, and the first typearg of Function is solely used for consumption.

Java is 'correct', your code is broken. What did you really mean?

Two options:

Option 1

If you intend to accept a function that can process any T, then it should be either a Function<T or a Function<? super T, as per PECS rules.

Option 2

Generics serve to link things, given that they are a figment of the compiler's imagination (java the VM doesn't know what they are, and most of the type args are erased entirely by javac). Perhaps you intended to link the type used in the first parameter with the type used in that function. Then.. make a type variable for that purpose, that is their job. You've already done that, so, fix up your bounds so they align with PECS:

public <T> List<Long> leafIds(Map<Long, ? extends List<? extends T>> childrenMap, Long parentId, Function<? super T, Long> idGetter) {

All I did was swap ? extends to ? super. What that says is:

There is some specific type. No idea what it is, though. Let's call it T.

The first argument gives us a map that maps to a list of Xs, where X is either that unknown type T or some subtype of that.

The third argument is a mapping function that is capable of converting some unknown type to something else. The unknown type it can convert is, however, guaranteed to be either T or some supertype of it.

That paints a complete, type-wise consistent picture. Let's say the map gives out Integers and the function can convert any Object (so, T is Number, the list are a subtype of T: Integer, check, and the mapping function can convert any Object, that's a supertype of T, so, check). That works out.

Now let's go in reverse:

The lists have Numbers in them, and the mapping function can only convert Doubles.

So what happens if one of the elements in the list is an Integer instead? Type safety broken.

Convert Iterable to Stream using Java 8 JDK

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.



Related Topics



Leave a reply



Submit