Well, “any” includes the possibility of “first”. Of course, the Stream implementation does not waste efforts in randomizing the data, so for a lot of cases, especially with sequential execution, it will still be the first element, if we can call it that way (as without an order, there is no distinguished first element).
Your best chances of exhibiting different results for findFirst
are with parallel Streams. But even there, not every combination of operations is suitable of exhibiting the unorderedness.
One point is, that in the current implementation, the findFirst()
operation does not change it’s behavior when the Stream is unordered, i.e. it does not actively try to be like findAny()
. It may still exhibit unpredictable behavior due to the source of the Stream, but if your source is Stream.of("this", "is", "a", "stream", "of", "strings")
, i.e. an immutable sequence of a known size, it has already the best parallel performance possible, so there’s simply no way of drawing a benefit of the chained unordered()
, hence, the current implementation doesn’t change its behavior.
It might surprise, but this even applies to a HashSet
to some degree. While it has an unspecified order, there will be an actual order within its backing array at some point of time and as long as you don’t modify the Set
, there will be no reason to shuffle these entries around, so for a particular HashSet
instance, you may repeatedly get the same “first” element, though it’s not specified which one and even within a single runtime, another HashSet
instance representing the same contents, but having a different history, may have a different order.
One example of an operation that is known to draw a benefit from the unordered characteristics is distinct
. While it has to sort out duplicates, it has to keep the first encountered of equal elements, if it makes a notable difference. This can degrade the performance significantly, hence, the implementation will immediately try to get a benefit if the stream is unordered. E.g.
List<String> equal=IntStream.range(0, 100)
.mapToObj(i->new String("test")) // don't do this in normal code
.collect(Collectors.toList());
Map<String, Integer> map = IntStream.range(0, equal.size())
.collect(IdentityHashMap::new, (m,i)->m.put(equal.get(i),i), Map::putAll);
equal.parallelStream().distinct().map(map::get)
.findFirst().ifPresent(System.out::println);
This creates a bunch of equal
but distinguishable String
instances (which you normally shouldn’t do), registers them with their positional number in an IdentityHashMap
, so we can find out, which instance distinct
has retained. Since the code above uses an ordered stream created by a List
, it consistently prints 0
, regardless of how often you execute it.
In contrast,
equal.parallelStream().unordered().distinct().map(map::get)
.findFirst().ifPresent(System.out::println);
will print arbitrary numbers of the range, as we have released the ordered contract and allow to pick any of the equal strings.
As already noted before, this is all implementation specific. You should never make an assumption about whether an operation can actually draw a benefit and thus, will change its behavior for unordered streams. The explanation above was only meant to illustrate why sometimes the behavior of a particular implementation might not change for unordered stream. Though, it still might in the next version or a different JRE implementation.