Reputation: 29510
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.
Upvotes: 640
Views: 368455
Reputation: 10818
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);
}
Upvotes: 2
Reputation: 328568
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);
A potentially more readable alternative 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);
Upvotes: 720
Reputation: 11707
I think that one answer using Apache Commons Lang is missing.
Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Stream<String> targetStream = org.apache.commons.lang3.stream.Streams.of(sourceIterator);
This works since Apache Commons Lang 3.13.0 (July 2023).
Upvotes: 4
Reputation: 31
Stream.iterate
does the job for you.
Simply iterate while it has elements and mapped them to next.
public <X> Stream<X> toStream(Iterator<X> iter) {
return Stream.iterate(iter, Iterator::hasNext, UnaryOperator.identity()).map(Iterator::next);
}
Upvotes: 3
Reputation: 16131
If you are using Spring (Data) then there is a utility class StreamUtils which has two methods:
createStreamFromIterator(Iterator<T> iterator)
createStreamFromIterator(CloseableIterator<T> iterator)
Upvotes: 2
Reputation: 1
if iteration size it's known this is possible:
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.**limit(3)**.forEach(System.out::println);
}
Upvotes: -1
Reputation: 1233
This is possible in Java 9.
Stream.generate(() -> null)
.takeWhile(x -> iterator.hasNext())
.map(n -> iterator.next())
.forEach(System.out::println);
Upvotes: 96
Reputation: 135
import com.google.common.collect.Streams;
and use Streams.stream(iterator)
:
Streams.stream(iterator)
.map(v-> function(v))
.collect(Collectors.toList());
Upvotes: 14
Reputation: 4240
Since version 21, Guava library provides Streams.stream(iterator)
It does what @assylias's answer shows.
Upvotes: 181
Reputation: 12942
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);
Upvotes: 11
Reputation: 1447
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());
Upvotes: 110