Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I turn a Stream into an Iterable? [duplicate]

Stream inherits an iterator() method to produce an Iterator.

But I need an Iterable rather than an Iterator.

For example, given this string:

String input = "this\n" +
        "that\n" +
        "the_other";

…I need to pass those parts of string as an Iterable to a specific library. Calling input.lines() yields a Stream. So I would be set if I could make that Stream into a Iterable of its elements.

like image 479
Basil Bourque Avatar asked Jan 22 '20 23:01

Basil Bourque


People also ask

What is the function used to duplicate a stream?

CopyTo(Stream) Reads the bytes from the current stream and writes them to another stream. Both streams positions are advanced by the number of bytes copied.

How do you reuse a stream in Java?

From the documentation: A stream should be operated on (invoking an intermediate or terminal stream operation) only once. A stream implementation may throw IllegalStateException if it detects that the stream is being reused. So the answer is no, streams are not meant to be reused.

Does stream use iterator Java?

In Java 8, we can use Stream. iterate to create stream values on demand, so called infinite stream.

How to convert iterable to stream in Java?

Given an Iterable, the task is to convert it into Stream in Java. Examples: Approach: Get the Iterable. Convert the Iterable to Spliterator using Iterable.spliterator() method. Convert the formed Spliterator into Sequential Stream using StreamSupport.stream() method. Return the stream.

What is the difference between iterator to stream and iterable to stream?

Iterator to Stream – Java 8 Iterator to stream follows the same path as Iterable to stream. The only difference is that the Iterator interface has no spliterator () method so we need to use Spliterators.spliteratorUnknownSize () method to get the spliterator.

What is the difference between iterator and SPL iterator in Java?

The only difference is that the Iterator interface has no spliterator () method so we need to use Spliterators.spliteratorUnknownSize () method to get the spliterator. Rest everything is same. 3. Iterator to Stream – Java 9


2 Answers

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));
like image 164
Holger Avatar answered Oct 14 '22 15:10

Holger


tl;dr

Just cast, no need to convert.

Cast Stream<String>::iterator to Iterable<String>.

Details

CAUTION See Answer by Holger explaining dangers of using a stream-backed Iterable.

Yes, you can make an Iterable from a Stream.

The solution is simple, but not obvious. See this post on Maurice Naftalin's Lambda FAQ.

The signature of the iterator method of BaseStream (superclass of Stream) returning a Iterator matches the only method of the functional interface Iterable, so the method reference Stream<T>::iterator can be used as an instance of Iterable<T>. (The fact that both methods have the same name is coincidental.)

Make your input.

String input = "this\n" +
        "that\n" +
        "the_other";
Stream<String> stream = input.lines() ;

Use the method reference to generate a Iterable<String>.

Iterable< String > iterable = stream::iterator;

Test the results.

for ( String s : iterable ) 
{
    System.out.println( "s = " + s );
}

See this code run live at IdeOne.com.

s = this

s = that

s = the_other

CAVEAT Beware of the risk of stream-backed Iterable. Explained in the correct Answer by Holger.

like image 31
Basil Bourque Avatar answered Oct 14 '22 16:10

Basil Bourque