Simplest way to stream an iterator

Bohemian picture Bohemian · Aug 31, 2015 · Viewed 7.7k times · Source

Say you want to stream the elements of an iterator; let's use a concrete example of a Scanner, which implements Iterator<String>.

Given an Iterator, say:

// Scanner implements Iterator<String>
Iterator<String> scanner = new Scanner(System.in);

Options to create a Stream<String> from it are the clunky:

StreamSupport.stream(
  Spliterators.spliteratorUnknownSize(scanner, Spliterator.ORDERED), false);

or the slightly more terse, but obtuse:

 StreamSupport.stream(
  ((Iterable<String>) () -> new Scanner(System.in)).spliterator(), false);

Is there a factory method somewhere in the JDK that returns a Stream<T> given an Iterator<T>?

Answer

Steve K picture Steve K · Oct 26, 2015

I would simply use Stream.generate(iterator::next). Yes, it is an infinite stream, but so is the scanner from System.in, unless you know how many lines you're asking for in which case it's easy enough to use

Stream.generate(iterator::next).limit(numLines);

This prevents the iterator from having to be iterated twice (once as the iterator, once as the stream).

If you need a sized stream without knowing how big it will be, but don't want to clutter your code just create a utility method that explicitly takes an Iterable<T>:

public static <T> Stream<T> stream(Iterable<T> it){
    return StreamSupport.stream(it.spliterator(), false);
}  

It ends up being more legible than trying to cram it all on one line, and then you can just call stream(()->iterator); or stream(()->new Scanner(System.in));

You can add the second convenience method easily enough, too:

public static <T> Stream<T> stream(Iterator<T> it){
    return stream(()->it);
}

enabling you to call stream(iterator); or stream(new Scanner(System.in));

Although, if I were going to read System.in into a stream, I'd probably use a Reader rather than a Scanner anyway:

return new BufferedReader(new InputStreamReader(System.in)).lines();