ChatGPT解决这个技术问题 Extra ChatGPT

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.

@DmitryGinzburg euh i don't want to create an "Infinite" Stream.
@DmitryGinzburg Stream.generate(iterator::next) works ?
@DmitryGinzburg That won't work for a finite iterator.

N
Neuron

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);

Stream are lazy: the code is only linking the Stream to the Iterator but the actual iteration won't happen until a terminal operation. If you use the iterator in the meantime you won't get the expected result. For example you can introduce a sourceIterator.next() before using the stream and you will see the effect (the first item will not be seen by the Stream).
@assylias, yeah it is really nice ! Maybe you could explains for future readers this quite magic line Iterable<String> iterable = () -> sourceIterator;. I have to admit that it took me some time to understand.
I should tell what I found. Iterable<T> is a FunctionalInterface which has only one abstract method iterator(). So () -> sourceIterator is a lambda expression instantiating an Iterable instance as an anonymous implementation.
Again, () -> sourceIterator; is a shortened form of new Iterable<>() { @Override public Iterator<String> iterator() { return sourceIterator; } }
@JinKwon It is not really a shortened form of an anonymous class (there are a few subtle differences, such as scope and how it is compiled) but it does behave similarly in this case.
S
Sled

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

It does what @assylias's answer shows.


Far better to use this consistently until such time as the JDK supports a native one-liner. It will be far simpler to find (hence refactor) this in future than the pure-JDK solutions shown elsewhere.
This is excellent but… how does Java have native iterators and streams… but no built-in, straightforward way to go from one to the other!? Quite an omission in my opinion.
M
Matan

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());

Why did this answer is at the end of the page?
D
Dónal

This is possible in Java 9.

Stream.generate(() -> null)
    .takeWhile(x -> iterator.hasNext())
    .map(n -> iterator.next())
    .forEach(System.out::println);

Simple, efficient and without resorting to subclassing - this should be the accepted answer!
Unfortunately these don't seem to work with .parallel() streams. They also appear a bit slower than going over Spliterator, even for sequential use.
Also, the first method throws up if the iterator is empty. The second method does work for now, but it does violate the requirement of the functions in map and takeWhile to be stateless, so I'd hesitate to do that in production code.
Really, this should be an accepted answer. Even though parallel might be funky the simpleness is amazing.
You really shouldn't be working with side effects and mutation.
T
Tunaki

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);

t
theletz
import com.google.common.collect.Streams;

and use Streams.stream(iterator) :

Streams.stream(iterator)
       .map(v-> function(v))
       .collect(Collectors.toList());

N
Neuron

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);

Code with side-effects isn't safe to call inside map or forEach.
M
Marinos An

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);
}

I
Israel C. S. Rocha

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


While short, this is very underperforming.
This will unfold the whole iterator to java object then convert it to stream. I dont suggest it
This seems to be only for enumerations, not iterators.
Not a terrible answer in general, useful in a pinch, but the question mentions performance and the answer is not performant.
Iterators and Streams are both lazy. Collecting the whole thing into a list before processing begins seems to miss the whole point.