I am looking for a concise way to convert an Iterator
to a Stream
or more specifically to "view" the iterator as a stream.
For performance reason, I would like to avoid a copy of the iterator in a new list:
Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Collection<String> copyList = new ArrayList<String>();
sourceIterator.forEachRemaining(copyList::add);
Stream<String> targetStream = copyList.stream();
Based on the some suggestions in the comments, I have also tried to use Stream.generate
:
public static void main(String[] args) throws Exception {
Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Stream<String> targetStream = Stream.generate(sourceIterator::next);
targetStream.forEach(System.out::println);
}
However, I get a NoSuchElementException
(since there is no invocation of hasNext
)
Exception in thread "main" java.util.NoSuchElementException
at java.util.AbstractList$Itr.next(AbstractList.java:364)
at Main$$Lambda$1/1175962212.get(Unknown Source)
at java.util.stream.StreamSpliterators$InfiniteSupplyingSpliterator$OfRef.tryAdvance(StreamSpliterators.java:1351)
at java.util.Spliterator.forEachRemaining(Spliterator.java:326)
at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
at Main.main(Main.java:20)
I have looked at StreamSupport
and Collections
but I didn't find anything.
Stream.generate(iterator::next)
works ?
One way is to create a Spliterator
from the Iterator
and use that as a basis for your stream:
Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Stream<String> targetStream = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(sourceIterator, Spliterator.ORDERED),
false);
An alternative which is maybe more readable is to use an Iterable
- and creating an Iterable
from an Iterator
is very easy with lambdas because Iterable
is a functional interface:
Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Iterable<String> iterable = () -> sourceIterator;
Stream<String> targetStream = StreamSupport.stream(iterable.spliterator(), false);
Since version 21, Guava library provides Streams.stream(iterator)
It does what @assylias's answer shows.
Great suggestion! Here's my reusable take on it:
public class StreamUtils {
public static <T> Stream<T> asStream(Iterator<T> sourceIterator) {
return asStream(sourceIterator, false);
}
public static <T> Stream<T> asStream(Iterator<T> sourceIterator, boolean parallel) {
Iterable<T> iterable = () -> sourceIterator;
return StreamSupport.stream(iterable.spliterator(), parallel);
}
}
And usage (make sure to statically import asStream):
List<String> aPrefixedStrings = asStream(sourceIterator)
.filter(t -> t.startsWith("A"))
.collect(toList());
This is possible in Java 9.
Stream.generate(() -> null)
.takeWhile(x -> iterator.hasNext())
.map(n -> iterator.next())
.forEach(System.out::println);
.parallel()
streams. They also appear a bit slower than going over Spliterator
, even for sequential use.
parallel
might be funky the simpleness is amazing.
Create Spliterator
from Iterator
using Spliterators
class contains more than one function for creating spliterator, for example here am using spliteratorUnknownSize
which is getting iterator as parameter, then create Stream using StreamSupport
Spliterator<Model> spliterator = Spliterators.spliteratorUnknownSize(
iterator, Spliterator.NONNULL);
Stream<Model> stream = StreamSupport.stream(spliterator, false);
import com.google.common.collect.Streams;
and use Streams.stream(iterator)
:
Streams.stream(iterator)
.map(v-> function(v))
.collect(Collectors.toList());
Another way to do this on Java 9+ using Stream::iterate(T, Predicate, UnaryOperator)
:
Stream.iterate(iterator, Iterator::hasNext, UnaryOperator.identity())
.map(Iterator::next)
.forEach(System.out::println);
map
or forEach
.
1 assylias's solution wrapped in a method:
public static <T> Stream<T> toStream(Iterator<T> iterator) {
return StreamSupport.stream(((Iterable<T>)() -> iterator).spliterator(), false);
}
2 guava Streams implementation (marked with @Beta
):
public static <T> Stream<T> stream(Iterator<T> iterator) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
}
Use Collections.list(iterator).stream()...
Success story sharing
sourceIterator.next()
before using the stream and you will see the effect (the first item will not be seen by the Stream).Iterable<String> iterable = () -> sourceIterator;
. I have to admit that it took me some time to understand.Iterable<T>
is aFunctionalInterface
which has only one abstract methoditerator()
. So() -> sourceIterator
is a lambda expression instantiating anIterable
instance as an anonymous implementation.() -> sourceIterator;
is a shortened form ofnew Iterable<>() { @Override public Iterator<String> iterator() { return sourceIterator; } }