How to Convert an Iterator to a Stream

How to convert an Iterator to a Stream?

One way is to create a Spliterator from the Iterator and use that as a basis for your stream:

Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();
Stream<String> targetStream = StreamSupport.stream(
Spliterators.spliteratorUnknownSize(sourceIterator, Spliterator.ORDERED),
false);

An alternative which is maybe more readable is to use an Iterable - and creating an Iterable from an Iterator is very easy with lambdas because Iterable is a functional interface:

Iterator<String> sourceIterator = Arrays.asList("A", "B", "C").iterator();

Iterable<String> iterable = () -> sourceIterator;
Stream<String> targetStream = StreamSupport.stream(iterable.spliterator(), false);

How to create a Java 8 Stream from an iterator?

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.

Make a Stream into an Iterable?

As explained in Why does Stream<T> not implement Iterable<T>?, an Iterable bears the expectation to be able to provide an Iterator more than once, which a Stream can’t fulfill. So while you can create an Iterable out of a Stream for an ad-hoc use, you have to be careful about whether attempts to iterate it multiple times could exist.

Since you said, “I need to pass those parts of string as an Iterable to a specific library”, there is no general solution as the code using the Iterable is outside your control.

But if you are the one who creates the stream, it is possible to create a valid Iterable which will simply repeat the stream construction every time an Iterator is requested:

Iterable<String> lines = () -> "this\nthat\nthe_other".lines().iterator();

This fulfills the expectation of supporting an arbitrary number of iterations, while not consuming more resources than a single stream when being traversed only once.

for(var s: lines) System.out.println(s);
lines.forEach(System.out::println);
System.out.println(String.join("\n", lines));

Convert Iterator to List

Better use a library like Guava:

import com.google.common.collect.Lists;

Iterator<Element> myIterator = ... //some iterator
List<Element> myList = Lists.newArrayList(myIterator);

Another Guava example:

ImmutableList.copyOf(myIterator);

or Apache Commons Collections:

import org.apache.commons.collections.IteratorUtils;

Iterator<Element> myIterator = ...//some iterator

List<Element> myList = IteratorUtils.toList(myIterator);

How do I convert an iterator into a stream on success or an empty stream on failure?

Rust is a statically typed language which means that the return type of a function has to be a single type, known at compile time. You are attempting to return multiple types, decided at runtime.

The closest solution to your original is to always return the Unfold stream:

fn resolve(addrs: impl ToSocketAddrs) -> impl Stream<Item = SocketAddr, Error = ()> {
stream::unfold(addrs.to_socket_addrs(), |r| {
match r {
Ok(mut iter) => iter.next().map(|addr| future::ok((addr, Ok(iter)))),
Err(_) => None,
}
})
}

But why reinvent the wheel?

futures::stream::iter_ok

Converts an Iterator into a Stream which is always ready to yield the next value.

Subsequent versions of the futures crate implement Stream for Either, which makes this very elegant:

fn resolve(addrs: impl ToSocketAddrs) -> impl Stream<Item = SocketAddr, Error = ()> {
match addrs.to_socket_addrs() {
Ok(iter) => stream::iter_ok(iter).left_stream(),
Err(_) => stream::empty().right_stream(),
}
}

It's straightforward to backport this functionality to futures 0.1 (maybe someone should submit it as a PR for those who are stuck on 0.1...):

enum MyEither<L, R> {
Left(L),
Right(R),
}

impl<L, R> Stream for MyEither<L, R>
where
L: Stream,
R: Stream<Item = L::Item, Error = L::Error>,
{
type Item = L::Item;
type Error = L::Error;

fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
match self {
MyEither::Left(l) => l.poll(),
MyEither::Right(r) => r.poll(),
}
}
}

trait EitherStreamExt {
fn left_stream<R>(self) -> MyEither<Self, R>
where
Self: Sized;
fn right_stream<L>(self) -> MyEither<L, Self>
where
Self: Sized;
}

impl<S: Stream> EitherStreamExt for S {
fn left_stream<R>(self) -> MyEither<Self, R> {
MyEither::Left(self)
}
fn right_stream<L>(self) -> MyEither<L, Self> {
MyEither::Right(self)
}
}

Even better, use the fact that Result is an iterator and Stream::flatten exists:

fn resolve(addrs: impl ToSocketAddrs) -> impl Stream<Item = SocketAddr, Error = ()> {
stream::iter_ok(addrs.to_socket_addrs())
.map(stream::iter_ok)
.flatten()
}

Or if you really want to print errors:

fn resolve(addrs: impl ToSocketAddrs) -> impl Stream<Item = SocketAddr, Error = ()> {
stream::once(addrs.to_socket_addrs())
.map(stream::iter_ok)
.map_err(|e| eprintln!("err: {}", e))
.flatten()
}

See also:

  • Conditionally return empty iterator from flat_map
  • Conditionally iterate over one of several possible iterators
  • What is the correct way to return an Iterator (or any other trait)?

Converting an AutoCloseable, Iterable class into a Stream

As stated in the javadoc, BaseStream.onClose() "Returns an equivalent stream with an additional close handler":

public class WorkingExample {
public static void main(final String[] args) {
MyCursor cursor = new MyCursor();
try (Stream<String> stream = StreamSupport.stream(cursor.spliterator(), false)
.onClose(cursor::close)) {
stream.forEach(System.out::println);
}
}
}

will call MyCursor.close() as desired.

Convert enhance loop with inner iterator to stream

One of possible solutions can be introducing an intermediate class to store intermediate values (assuming Month is an enum):

private static Map<Month, Integer> sumGroupingByMonth(final Collection<Scooter> scooters) {
@AllArgsConstructor
final class Intermediate {
final int newVal;
final Day rentalDay;
}

return scooters.stream()
.flatMap(scooter -> {
final int newVal = scooter.getMileage() * scooter.getMileageRate();
return scooter.getRentalDays()
.stream()
.map(day -> new Intermediate(newVal, day));
})
.collect(Collectors.groupingBy(intermediate -> intermediate.rentalDay.getMonth(), Collectors.summingInt(intermediate -> intermediate.newVal)));
}

I find this code pretty complex: closures, explicit intermediate state, function scoping, intermediate objects, and probably the following could be both more simple and faster?

private static int[] sumGroupingByMonth(final Iterable<Scooter> scooters) {
final int[] data = new int[12];
for ( final Scooter scooter : scooters ) {
final int newVal = scooter.getMileage() * scooter.getMileageRate();
for ( final Day rentalDay : scooter.getRentalDays() ) {
data[rentalDay.getMonth().ordinal()] += newVal;
}
}
return data;
}


Related Topics



Leave a reply



Submit