Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simplest way to stream an iterator

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>?

like image 622
Bohemian Avatar asked Aug 31 '15 18:08

Bohemian


People also ask

What are the 2 methods that an iterator needs to implement?

To iterate, hasNext() and next() methods are used in a loop. Classes that implement the Iterable interface need to override the iterator() method. Classes that implement Iterator interface need to override hasNext(), next() and remove() methods.

Can you stream an iterable?

The Iterable interface is designed keeping generality in mind and does not provide any stream() method on its own. Simply put, you can pass it to StreamSupport. stream() method and get a Stream from the given Iterable instance.


1 Answers

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();
like image 115
Steve K Avatar answered Oct 30 '22 04:10

Steve K