sdenham
sdenham

Reputation: 545

How to create a Java 8 Stream from an iterator?

Is it possible to create a Stream from an Iterator, in which the sequence of objects is the same as that generated by calling the iterator's next() method repeatedly? The specific case I am thinking of concerns the use of the iterator returned by TreeSet.descendingIterator(), but I can imagine other circumstances in which an iterator, but not the collection it references, is available.

For example, for a TreeSet<T> tset we can write tset.stream()... and get a stream of the objects in that set, in the set's sort order, but what if we want them in a different order, such as that available through using descendingIterator()? I am imagining something like tset.descendingIterator().stream()... or stream( tset.descendingIterator() )..., though neither of these forms are valid.

Upvotes: 34

Views: 35050

Answers (4)

lczapski
lczapski

Reputation: 4140

As it was written by Karol Król for infinite stream you can use this:

Stream.generate(iterator::next)

but you can also use it for finite stream with takeWhile since Java 9

Stream.generate(iterator::next).takeWhile((v) -> iterator.hasNext())

Upvotes: 0

Karol Kr&#243;l
Karol Kr&#243;l

Reputation: 3530

static <T> Stream<T> iteratorToFiniteStream(final Iterator<T> iterator) {
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false);
}

static <T> Stream<T> iteratorToInfiniteStream(final Iterator<T> iterator) {
    return Stream.generate(iterator::next);
}

Upvotes: 56

Jordan Shurmer
Jordan Shurmer

Reputation: 1066

This doesn't create a stream, but Iterator also has a method called forEachRemaining:

someIterator.forEachRemaining(System.out::println);
someIterator.forEachRemaining(s -> s.doSomething());
//etc.

The argument you pass in is a Consumer which is the same thing you pass to Stream::forEach.

Here are the docs for that method. note that you can't continue the "chain" like you can with a stream. But I've still found this helpful the few times I've wanted a Stream from an Iterator.

Upvotes: 3

andersschuller
andersschuller

Reputation: 13907

For the particular example of NavigableSet.descendingIterator(), I think the simplest way is to use NavigableSet.descendingSet() instead.

But given you are probably interested in the more general case, the following seems to work:

import java.util.Iterator;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.TreeSet;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class Streams {
    public static void main(String... args) {
        TreeSet<String> set = new TreeSet<>();
        set.add("C");
        set.add("A");
        set.add("B");

        Iterator<String> iterator = set.descendingIterator();

        int characteristics = Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED;
        Spliterator<String> spliterator = Spliterators.spliteratorUnknownSize(iterator, characteristics);

        boolean parallel = false;
        Stream<String> stream = StreamSupport.stream(spliterator, parallel);

        stream.forEach(System.out::println); // prints C, then B, then A
    }
}

In short, you have to create a Spliterator from the Iterator first using one of the static methods in Spliterators. Then you can create a Stream using the static methods in StreamSupport.

I don't have that much experience with creating Spliterators and Streams by hand yet, so I can't really comment on what the characteristics should be or what effect they will have. In this particular simple example, it didn't seem to matter whether I defined the characteristics as above, or whether I set it to 0 (i.e. no characteristics). There is also a method in Spliterators for creating a Spliterator with an initial size estimate - I suppose in this particular example you could use set.size(), but if you want to handle arbitrary Iterators I guess this won't be the case. Again, I'm not quite sure what effect it has on performance.

Upvotes: 36

Related Questions