Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 226
sdenham Avatar asked May 03 '14 03:05

sdenham


People also ask

Does stream use Iterator java?

In Java 8, we can use Stream. iterate to create stream values on demand, so called infinite stream.

What is the difference between a stream and an Iterator?

Iterators, in Java, are used in Collection Framework to retrieve elements one by one. A stream in Java is a pipeline of objects from an array or a collection data source. A sequential stream is one in which the objects are pipelined in a single stream on the same processing system.

How do I stream iterable?

Converting Iterable to Stream 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.


3 Answers

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);
}
like image 72
Karol Król Avatar answered Oct 06 '22 22:10

Karol Król


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.

like image 36
andersschuller Avatar answered Oct 06 '22 23:10

andersschuller


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.

like image 3
Jordan Shurmer Avatar answered Oct 07 '22 00:10

Jordan Shurmer