nantitv
nantitv

Reputation: 3733

Encounter order preservation in java stream

I have gone through related questions like How to ensure order of processing in java8 streams?, still ordering of output element is not completely clear to me. Therefore please clarify my following doubt.

 Integer[] intArray = {1, 2, 3, 4, 5, 6, 7, 8 };
            List<Integer> listOfIntegers =
                new ArrayList<>(Arrays.asList(intArray));
       listOfIntegers
            .parallelStream()
             .unordered()
            .forEachOrdered(e -> System.out.print(e + " "));

I think at least theoretically(or according to java specification) it can print in random order than 1, 2, 3, 4, 5, 6, 7, 8. Am I correct?

One more related question- the decision of the encounter order preservation is taken at what point of execution? To be more precise - is the evaluation of the entire stream pipeline ORDER characteristic done by going through the characteristics of source, intermediate operations and terminal operation even before the start of the execution?

Upvotes: 3

Views: 1721

Answers (2)

Holger
Holger

Reputation: 298123

The unordered nature of a source or the explicit releasing of the order contract via unordered() may affect all subsequent pipeline stages unless they introduce an order which can only happen with a sorted operation.

For stateless intermediate operations like filter and map, there is no difference anyway, but operations like skip, limit and distinct may exhibit a different behavior depending on whether the previous stream state was ordered or unordered. This answer shows an example of how distinct can be affected by a previous unordered().

Note that in principle, sorted, while introducing an order, may depend on the ordered state of previous stage, as it may use an unstable sort algorithm if the previous stream was unordered.

This answer provides a way to print the characteristics of a stream and evaluate how they change due to appending another operation.

When you chain a terminal operation, both, an unordered nature of the terminal operation itself or an unordered state of the last stage before the terminal operation may be sufficient to select an algorithm for the terminal operation that doesn’t try to preserve the order.

In principle, the unordered nature of the terminal operation could be used to affect previous stages, but since stateless intermediate operations are not affected anyway and skip, limit, distinct have to obey a previous ordered state, if present, the only operation that could be affected, is sorted that becomes obsolete if the subsequent operations don’t care about the order anyway.

In the current implementation, since Java 8 update 60, the unordered nature of a terminal operation does not affect the behavior of previous stages. This change was made, as in previous implemen­tations, it wrongly affected skip and limit. Loosing the opportunity to elide obsolete sorting steps was not considered a problem, as chaining sort with unordered subsequent operations, is a corner case. See this answer, including the comments, if you want to know more about the related discussion.

So for

list.stream() // List.stream() returns an ordered stream
    .unordered() // releases order contract
    .distinct() // for equal elements, it may pick an arbitrary one
    .sorted() // re-introduces an order
    .skip(1) // will skip the minimum element due to the order
    .forEach(System.out::println); // may print the remaining elements in arbitrary order

there is not a single ordered or unordered behavior for the stream pipeline.

In contrast, with

hashSet.stream() // HashSet.stream() has no order (unless being a LinkedHashSet)
    .filter(Objects::nonNull) // not affected by order
    .distinct() // may use unorderedness, but has no effect anyway, as already distinct
    .skip(1) // may skip an arbitrary element
    .forEachOrdered(System.out::println); // would respect order if there was one

the entire pipeline runs unordered, just because the source is unordered. With an ordered source, it would be entirely ordered.

So the answer to “is the evaluation of the entire stream pipeline ORDER characteristic done by going through the characteristics of source, intermediate operations and terminal operation even before the start of the execution?” is, yes, this is done right before starting the actual processing, by selecting the appropriate algorithm for the pipeline stages, when there is a choice, but this process does not necessarily result in a single characteristic for the entire pipeline.

Upvotes: 6

Tim B
Tim B

Reputation: 41178

As soon as you selected unordered then the end result can come through in essentially a random order. Note though that there is no requirement that it do, so in fact it is likely you will still see some ordering in the output.

forEachOrdered preserves the encounter order of the 'stream' so if you didn't have .unordered() then it would ensure you see the elements in encounter order. If the stream is already unordered though then it is pointless and you might as well use forEach.

In other words forEachOrdered preserves the encounter order in an already ordered stream. It does not do any sorting or other ordering though so if the stream is already unordered then anything can happen.

Upvotes: 2

Related Questions