HuntExams Academy logo
JavaIntermediate#streams

What is the difference between findFirst() and findAny() in streams?

Answer

findFirst() returns the first element in encounter order, deterministic for ordered streams. findAny() returns any matching element and may be faster in parallel streams since it doesn't need to preserve order.

Example

Optional<Integer> first = List.of(1,2,3).stream().filter(x -> x > 1).findFirst(); // 2
Optional<Integer> any = List.of(1,2,3).parallelStream().filter(x -> x > 1).findAny(); // 2 or 3

Detailed explanation

findFirst() and findAny() are short-circuiting terminal operations in Java streams. Both return an Optional<T> containing a selected element, or Optional.empty() if the stream has no elements remaining after intermediate operations such as filter(). The main difference is their selection guarantee: findFirst() respects encounter order when one exists, whereas findAny() may select any element. Neither method accepts a predicate directly; to find an element satisfying a condition, use filter(predicate) before the terminal operation. Once either operation executes, that stream cannot be reused.

Encounter order is the logical order established by the stream source and operations, not the order in which worker threads finish. A List stream has encounter order, so List.of(1, 2, 3).stream().filter(x -> x > 1).findFirst() returns Optional containing 2. The same pipeline using parallelStream() and findFirst() must still return 2. A source such as HashSet provides no defined encounter order, so findFirst() does not guarantee a particular value there. Operations such as sorted() can establish an encounter order, while unordered() removes the ordering constraint without necessarily shuffling elements.

findAny() deliberately gives the implementation freedom to select whichever element is convenient. For List.of(1, 2, 3).parallelStream().filter(x -> x > 1).findAny(), either 2 or 3 is valid. In parallel execution, stream work is split into tasks, and findAny() can accept a result without establishing that no earlier encounter-order element qualifies. Ordered findFirst() requires additional coordination to preserve that guarantee. Consequently, findAny() may perform better in parallel pipelines, but this is not guaranteed: input size, filtering cost, splitting overhead, and upstream operations all affect performance.

Choose findFirst() when the earliest qualifying element has business meaning, such as the first eligible entry in a priority-ordered list. Choose findAny() when every qualifying element is equally acceptable. In sequential execution, findAny() often returns the first element, but its contract does not promise this behaviour or randomness. Interviewers generally expect you to distinguish contractual guarantees from observed output, explain the parallel-performance trade-off, and handle the Optional safely rather than calling get() without checking whether a result exists.

Key points to remember

  • Both are short-circuiting terminal operations returning Optional<T>; an empty result produces Optional.empty().
  • findFirst() returns the earliest element in encounter order, including during parallel execution.
  • Without encounter order, findFirst() may return any element.
  • findAny() provides no first-element guarantee, even on an ordered sequential stream.
  • findAny() can reduce ordering-related coordination in parallel streams, but is not always faster.

Common mistakes candidates make

  • Claiming findAny() returns a random element or must produce different results across runs.
  • Assuming parallel execution makes findFirst() ignore an ordered stream's encounter order.
  • Calling Optional.get() unconditionally instead of handling an empty result with methods such as orElse(), orElseThrow(), or ifPresent().

Likely follow-up questions

Does unordered().findFirst() behave like findAny()?

Removing encounter order means findFirst() is no longer required to select the original first element. However, unordered() does not shuffle the stream or guarantee a performance improvement; findAny() states the any-element intent more directly.

Does short-circuiting mean only one matching element is processed?

No; it means the operation can finish without consuming all input. Parallel tasks may already be processing other elements, and upstream operations such as sorted() may need to consume the entire input before producing a result.

What happens if the selected element is null?

Both methods throw NullPointerException if the selected element is null, because Optional cannot hold a present null value. If nulls are possible and should be ignored, add filter(Objects::nonNull) before selection.

Interview tip: Explain the encounter-order guarantee first, demonstrate the list example, and then qualify the potential parallel-performance benefit.

Related Interview Questions

1
JavaBeginner#java8

What is the difference between Optional.of() and Optional.ofNullable()?

Open
2
JavaIntermediate#java8#lambda

What is a method reference and what are its types?

Open
3
JavaIntermediate#streams

What is the difference between Stream.reduce() with and without an identity value?

Open