gontard
gontard

Reputation: 29510

How to convert an Iterator to a Stream?

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

Answers (12)

Marinos An
Marinos An

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

assylias
assylias

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

LaurentG
LaurentG

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

Keegan Carlo Falcao
Keegan Carlo Falcao

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

Robert Niestroj
Robert Niestroj

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)

https://docs.spring.io/spring-data/commons/docs/3.0.0/api/org/springframework/data/util/StreamUtils.html#createStreamFromIterator(java.util.Iterator)

Upvotes: 2

Chavi
Chavi

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

PhilipRoman
PhilipRoman

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

sneha
sneha

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

num&#233;ro6
num&#233;ro6

Reputation: 4240

Since version 21, Guava library provides Streams.stream(iterator)

It does what @assylias's answer shows.

Upvotes: 181

Israel C. S. Rocha
Israel C. S. Rocha

Reputation: 87

Use Collections.list(iterator).stream()...

Upvotes: -6

Bassem Reda Zohdy
Bassem Reda Zohdy

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

Matan
Matan

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

Related Questions