gontard
gontard

Reputation: 29530

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: 370751

Answers (13)

SurajK AtWork
SurajK AtWork

Reputation: 1

I prefer two static methods
1. For Iterator object

public static <T> Stream<T> of(Iterator<T> iterator) {
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator,
    Spliterator.ORDERED | Spliterator.IMMUTABLE), false);}


2. For iterable object

 public static <T> Stream<T> of(Iterable<T> iterable) {
return StreamUtil.of(iterable.iterator());}

Upvotes: 0

Marinos An
Marinos An

Reputation: 10878

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: 328835

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: 721

LaurentG
LaurentG

Reputation: 11797

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: 5

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: 16151

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: 4250

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: 1457

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: 111

Related Questions