Java 8 Stream – FindFirst and FindAny

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 

  1. Source of the stream and 
  2. 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.

References

Leave a Reply