Introduction
The Java 8 Streams has two methods, findFirst and findAny which will return the first element and an arbitrary element from a stream respectively. This post is on those Stream methods. We will also look at how they behave for ordered and unordered streams.
Stream findFirst and findAny
First, let us look at the Java 8 Stream’s findFirst and findAny methods at a high-level. Both of these methods are short-circuiting terminal operations.
Stream#findFirst
The findFirst method returns an Optional which describes the first element of the stream. It will return an empty Optional if the stream is empty. For unordered streams, it can return any element.
Stream#findAny
Contrary to the findFirst method, the findAll method by default can return any element of the stream (wrapped in an Optional). This method can pick any element in the stream and return that. Hence, multiple invocations of this method may not return the same result. As we will see, this method is useful when we have a parallel stream.
What is encounter order of a stream
Streams created in Java may or may not have an encounter order. It depends on the
- Source of the stream and
- The intermediate operations applied on the stream
For example, streams created from a List are ordered whereas streams from a HashSet are unordered. If we sort a stream, it becomes ordered and we can mark any stream as unordered by calling the unordered method on the stream.
As we will see, the element that the findFirst method returns depends on the encounter order of the stream. When we have an ordered stream, we have a predictable order in which the elements flow through the rest of the stages in the stream pipeline. But for an unordered stream, the order in which the elements will be processed cannot be determined, and it can vary from execution to execution.
Important note: The encounter order is different from whether the stream is sequential or parallel.
Examples for Stream#findFirst
Sequential, ordered stream
Let us create a list using List#of method. Since a stream created from a list is ordered, calling the findFirst should always return the first element of the stream. In the shown example, it always returns 1.
List<Integer> ints = List.of(1, 2, 3, 4, 5);
Optional<Integer> firstResult = ints.stream()
.findFirst();
System.out.println(firstResult); //Optional[1]
Let us look at another example. We created an IntStream containing numbers 1 to 4, mapped each to twice of its value and get the first element from the stream. Since the stream created is ordered and there are no intermediate operations to change the order, we get the result as 2 i.e., the first element mapped (1 multiplied by 2).
Optional<Integer> firstResult = IntStream.range(1, 5)
.mapToObj(i -> i * 2)
.findFirst();
System.out.println(firstResult); //Optional[2]
For an empty stream, it returns an empty optional.
List<Integer> ints = List.of();
Optional<Integer> firstResult = ints
.stream()
.findFirst();
System.out.println(firstResult); //Optional.empty
Sequential, unordered stream
Let us move onto using findFirst on an unordered sequential stream. First, let us create a stream out of a List and make it unordered.
List<Integer> ints = List.of(1, 2, 3, 4, 5);
Optional<Integer> firstResult = ints.stream()
.unordered() //make the stream unordered
.findFirst();
System.out.println(firstResult); //Optional[1]*
//* not guaranteed
Note that calling unordered does not necessarily change the order of the stream. It only removes the ordering constraint on the stream pipeline. But if we run this, it seems to always return the first element as per encounter order. But this is not guaranteed and not to be relied upon. The findFirst can return any element if the stream is unordered.
Let us create a truly unordered stream from a Set. The stream created from a Set is unordered and can have different encounter order each time.
Set<Integer> set = Set.of(1, 2, 3, 4, 5);
Optional<Integer> firstResult = set.stream()
.findFirst();
System.out.println(firstResult);
Run the above code and you can see that the output keeps changing. It could return any element as the first element.
Parallel, ordered stream
Let us test the findFirst on parallel streams. Using findFirst on a parallel ordered stream affects the performance because since the stream is ordered, it has to return the (real) first element from a parallel pipeline. This undermines the usage of a parallel stream.
List<Integer> ints = List.of(1, 2, 3, 4, 5);
//affects performance.. always returns Optiona[1]
Optional<Integer> firstResult = ints.stream()
.parallel()
.findFirst();
System.out.println(firstResult);
Parallel, unordered stream
We can make the stream as unordered to improve performance. Now, even though we have a parallel stream, since it is unordered, it can return any element back.
Optional<Integer> firstResult = ints.stream()
.parallel()
.unordered()
.findFirst();
System.out.println(firstResult); //can return any element
The same applies to a parallel stream created from a Set.
Set<Integer> set = Set.of(1, 2, 3, 4, 5);
Optional<Integer> firstResult = set.stream()
.parallel()
.findFirst();
System.out.println(firstResult); //can return any element
Examples for Stream#findAny
Let us look at applying the same set of scenarios to the findAny method.
Sequential, ordered stream
For a sequential ordered stream, it seems to always return the first element. But as we saw in the definition, findAny can return any element of the stream.
List<Integer> ints = List.of(1, 2, 3, 4, 5);
Optional<Integer> firstResult = ints.stream()
.findAny();
System.out.println(firstResult); //Optional[1]*
//* not guaranteed
For empty streams, it will return an empty Optional.
List<Integer> ints = List.of();
Optional<Integer> firstResult = ints
.stream()
.findAny();
System.out.println(firstResult); //Optional.empty
Sequential, unordered stream
Let us make the stream from a List as unordered. Again, it might appear to always return the first element, but we cannot guarantee it.
List<Integer> ints = List.of(1, 2, 3, 4, 5);
Optional<Integer> firstResult = ints.stream()
.unordered()
.findAny();
System.out.println(firstResult); //Optional[1]*
//* not guaranteed
For an unordered stream created out of a Set, it can return any element.
Set<Integer> set = Set.of(1, 2, 3, 4, 5);
Optional<Integer> firstResult = set.stream()
.findAny();
System.out.println(firstResult); //can return any element
Parallel, ordered stream
For a parallel stream, findAny is more suitable than findFirst as it doesn’t suffer from the performance problem described earlier. This is because, as per the contract of findAny, it can return any element. Hence, for a parallel ordered stream, it returns an arbitrary element for each run.
List<Integer> ints = List.of(1, 2, 3, 4, 5);
Optional<Integer> firstResult = ints.stream()
.parallel()
.findAny();
System.out.println(firstResult); //can return anything
Parallel, unordered stream
For parallel unordered streams too, it can return any element. See the examples shown below.
Optional<Integer> firstResult = ints.stream()
.parallel()
.unordered()
.findAny();
System.out.println(firstResult); //can return anything
Set<Integer> set = Set.of(1, 2, 3, 4, 5);
Optional<Integer> firstResult = set.stream()
.parallel()
.findAny();
System.out.println(firstResult); //can return anything
Summary of findFirst and findAny methods of Java 8 streams
For sequential streams, encounter order does not affect the performance of the findFirst method. It only affects which element would be returned back. For parallel ordered streams, using findFirst is not useful as it has to return the first element as per encounter order. Thus for parallel ordered streams, if we can relax the ordering constraint (by calling unordered()), we can make the execution more efficient.
The findAny method works well (no performance issue) for all kinds of streams.
Conclusion
We have seen the findFirst and findAny methods of Java 8 Streams with examples in this post. Make sure to check out the Java stream posts.