Why can Flux.fromIterable(Iterable) call Iterable.iterator() twice?

218 Views Asked by At

The Flux.fromIterable(Iterable) has an interesting sentence in its contract:

The Iterable.iterator() method will be invoked at least once and at most twice for each subscriber.

What is the scenario when the method needs to call the Iterable.iterator() twice?

There is an explanation for it in the documentation, however I don't fully understand it:

This operator inspects the Iterable's Spliterator to assess if the iteration can be guaranteed to be finite. Since the default Spliterator wraps the Iterator we can have two Iterable.iterator() calls. This second invocation is skipped on a Collection however, a type which is assumed to be always finite.

1

There are 1 best solutions below

1
gdomo On

Until that issue was recently resolved, Iterable.iterator() was actually called twice. Here is FluxIterable source code of reactor-core 3.4.25:

@Override
public void subscribe(CoreSubscriber<? super T> actual) {
    // ...
    try {
        knownToBeFinite = FluxIterable.checkFinite(iterable);
        it = iterable.iterator();
    }
    // ...
}

while FluxIterable.checkFinite(iterable) is

static <T> boolean checkFinite(Iterable<T> iterable) {
    return iterable instanceof Collection || iterable.spliterator().hasCharacteristics(Spliterator.SIZED);
}

and Iterable.spliterator is

default Spliterator<T> spliterator() {
    return Spliterators.spliteratorUnknownSize(iterator(), 0);
}

So the first call to .iterator is made to obtain an Spliterator and examine whether it's finite, and the second call is performed in order to actually obtain iterator that would be used to iterate over the elements.

Since mentioned issue has been resolved, I'm not sure whether that notice about two iterator() calls is still valid. May be calling iterator() only once now is a kind of undocumented feature that is not reliable in feature.