简体   繁体   中英

iterator() on parallel stream guarantee encounter order?

Stream.of(a, b, c).parallel().map(Object::toString).iterator();

Is the returned iterator guaranteed to provide the values a , b , c in that order?

I'm aware toArray() and collect() guarantee collections with values in the correct order. Also, I'm not asking how to make a stream from an iterator.

This is an oversight in the specification. If a stream has a defined encounter order, the intent was that its Iterator produce the elements in encounter order. If the stream has no defined encounter order, the Iterator will of course produce the elements in some order, but that order won't be defined.

I've filed bug JDK-8194952 to track the change to the specification.

It looks like others have crawled through enough of the implementation to show that it will indeed produce the elements in encounter order. In addition, our stream tests rely on this property. For example, the test for the toList collector asserts that the elements in the list are present in the same order as they are obtained from the stream's Iterator. So it's probably safe for you to rely on this behavior, even though it isn't formally specified (yet).

The Stream.of method , used to create a stream from otherwise un-associated values, returns an sequential, ordered stream.

Returns a sequential ordered stream whose elements are the specified values.

According to the package Javadocs for java.util.stream , Side Effects section:

IntStream.range(0,5).parallel().map(x -> x*2).toArray() must produce [0, 2, 4, 6, 8]

This implies that parallel() and map() preserve whether the stream is sequential/ordered.

I've traced the implementation of the Stream that Stream.of creates to a class called ReferencePipeline .

@Override
public final Iterator<P_OUT> iterator() {
    return Spliterators.iterator(spliterator());
}

That implementation's iterator() method defers to Spliterator.iterator() , whose code adapts to the Iterator interface by simply relying on the Spliterator 's tryAdvance method, and does not change any stream characteristics:

public static<T> Iterator<T> iterator(Spliterator<? extends T> 
    spliterator) {
    Objects.requireNonNull(spliterator);
    class Adapter implements Iterator<T>, Consumer<T> {
        boolean valueReady = false;
        T nextElement;

        @Override
        public void accept(T t) {
            valueReady = true;
            nextElement = t;
        }

        @Override
        public boolean hasNext() {
            if (!valueReady)
                spliterator.tryAdvance(this);
            return valueReady;
        }

        @Override
        public T next() {
            if (!valueReady && !hasNext())
                throw new NoSuchElementException();
            else {
                valueReady = false;
                return nextElement;
            }
        }
    }

    return new Adapter();
}

In conclusion, yes, the order is guaranteed because Stream.of creates a "sequential ordered stream", and none of the operations you use above: parallel , map , or iterator change the characteristics. In fact, iterator uses the underlying Stream Spliterator to iterate over the stream elements.

The closest to a guarantee that I have found so far ist the following statement in the package documentaion for java.util.stream :

Except for operations identified as explicitly nondeterministic, such as findAny(), whether a stream executes sequentially or in parallel should not change the result of the computation.

Arguably, iterator() producing an Iterator iterating in a different order would be a "change in the result", just as much as producing a List containing elements in a different order would be for collect() .

Yes it will. And that is because terminal operations, unless otherwise specified in the documentation ( forEach for example - that explicitly specifies this to be non-deterministic vs forEachOrdered ), they do preserve encounter order. And your Stream.of does return an ordered stream; order that is not broken anywhere (via unordered for example, or sorted/distinct )

Given the fact the Stream.of returns, an ordered stream as stated in the documentation and none of the intermediate operations you've shown change this behavior we can, therefore, say that the returned iterator is guaranteed to provide the values 2, 3, 4 in order when enumerated because calling the iterator terminal operation on an ordered stream or sequence (eg a List implementation) should produce elements in that order when enumerated over.

So it shouldn't matter whether the code is executed sequentially or in parallel as long as we have an ordered source, intermediate operation(s) & terminal operation which respects this order then the order should be maintained.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM