Reputation: 545
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
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
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
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
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