4

While working on a Java project, I came across code that looked like this:

someMap.keySet().stream().sorted().forEach(/* ... */);

The intention here is clearly to do something for each key in the map, according to the keys' natural order, and this does seem to be what happens in practice. However, I'm not sure if this behavior is guaranteed. The Javadoc for Stream#forEach says:

The behavior of this operation is explicitly nondeterministic. For parallel stream pipelines, this operation does not guarantee to respect the encounter order of the stream, as doing so would sacrifice the benefit of parallelism. For any given element, the action may be performed at whatever time and in whatever thread the library chooses.

I know that if the code were using .parallelStream() instead of .stream() that it wouldn't be guaranteed to work, but since it is using a sequential stream (which the Javadoc doesn't say anything about), I'm not sure. Is this guaranteed to always work, or would that code need to be using .forEachOrdered() instead of .forEach() for it to be?

EDIT: I believe that this question is not a duplicate of forEach vs forEachOrdered in Java 8 Stream, because that question is asking "what's an example of a difference between forEach and forEachOrdered in general", and the accepted answer is basically "parallel streams". This question is specifically about sequential streams.

  • *streams API* is the *functional programming* interface of Java. In *functional programming* the basic idea is that elements do not have any relationship to each other. Even order does not matter. Order may only matter for *display* but not for any processing of individual elements as you do with `forEach()`. – Timothy Truckle Dec 31 '17 at 21:09
  • In fact it would be a nice optimization if using `forEach` - and thus an unordered pipeline - optimized away the sorting. – the8472 Jan 01 '18 at 06:45

3 Answers3

10

It's not guaranteed that the forEach terminal operation will process elements in the encounter order hence the "is explicitly nondeterministic". Although under the current implementation it should process the elements of a sequential stream in the encounter order of the stream.

forEachOrdered is primarily for cases where you're using a parallel stream and want to respect the encounter order of the stream if the stream has a defined encounter order.

Using forEach or forEachOrdered on a sequential stream will have the same effect so it's a matter of preference.

As mentioned above, under the current implementation we know that the forEach terminal operation should process the elements of a sequential stream in the encounter order of the stream but since it's not stated in the java doc it's better to sit on the fence and use forEachOrdered if you really care about the iteration order.

Ousmane D.
  • 54,915
  • 8
  • 91
  • 126
  • 4
    It currently provides that behaviour, and I would expect that it always will, but as it's not guaranteed in the contract I wouldn't rely on it. I agree with [this answer](https://stackoverflow.com/a/32797682/1898563). If you care about the iteration order, use the method which says you care about the iteration order. There's literally no reason not to, besides saving yourself 7 characters. – Michael Dec 31 '17 at 20:57
  • @Michael that's actually a good point and will add some content to my answer for _If you care about the iteration order, use the method which says you care about the iteration order._ – Ousmane D. Dec 31 '17 at 21:01
4

Under the current implementation it is - but the documentation is pretty clear not to specify this as a rule. This obviously can change, but at the moment it does not, even when doing:

Stream.of(5, 4, 3, 1)
      .unordered()
      .forEach(System.out::println);

Even if you are breaking the order on purpose, for sequential streams, data is not shuffled on purpose internally - at least not at the moment.

You have to be careful though, not to rely on it as things can change between versions, here is one example

Eugene
  • 117,005
  • 15
  • 201
  • 306
0

Is this guaranteed to always work, or would that code need to be using .forEachOrdered() instead of .forEach() for it to be?

No. It is not guaranteed to always work. As the javadoc says, the forEach() method may not be deterministic. This means that the implementors have the license to change a case that is currently deterministic to behave differently.

If you want guaranteed determinism, use the method where the javadoc explicitly guarantees it. It is highly unlikely that that the explicit guarantee of order preservation in .forEachOrdered() would ever deliberately be broken in a future release.


So ... I would point out that mistake to the maintainers of that application. We cannot predict that it will break in the future, but it certainly could. Latent bugs are bugs.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216