Question 26 · Section 8

What do findFirst() and findAny() operations do?

Both operations find the first matching element and return Optional:

Language versions: English Russian Ukrainian

🟢 Junior Level

Both operations find the first matching element and return Optional<T>:

findFirst() — returns the first element in stream order:

Optional<String> first = list.stream()
    .filter(s -> s.startsWith("A"))
    .findFirst();

findAny() — returns any matching element:

Optional<String> any = list.stream()
    .filter(s -> s.startsWith("A"))
    .findAny();

findFirst() — always the first element BY ORDER (guaranteed for ordered streams). findAny() — ANY element (can be any, optimization for parallelStream). In a sequential stream, findAny() often equals findFirst(), but NOT guaranteed.

In a regular (sequential) stream, both work the same. The difference appears in parallelStream.

Important: They return Optional — you must handle the empty stream case.

🟡 Middle Level

Internal differences

findFirst():

  • Strictly respects element order (Encounter Order)
  • In a parallel stream — an expensive operation. Threads coordinate to guarantee the result from the “leftmost” chunk

findAny():

  • Can return any matching element
  • In a parallel stream — the ideal choice. As soon as ANY thread finds a match — the result is returned immediately

When to choose findAny?

In high-load, findAny() is almost always preferable if the specific instance does not matter:

  • Finding any free worker in a pool
  • Any active user session

Removes unnecessary synchronization → faster on multi-core systems.

// In parallelStream, findAny() can return any element from any worker.
// Do NOT use findAny() if the specific element matters — only findFirst().

When NOT to use findFirst/findAny

  1. You need to check existenceanyMatch() is more efficient (does not create an Optional wrapper)
  2. You need ALL elementscollect(), not find
  3. findAny() when order matters — result is unpredictable in parallelStream

🔴 Senior Level

Short-circuiting coordination

When called on millions of elements, the Stream API uses Sink.cancellationRequested():

  • Immediately terminates all previous stages (filter, map)
  • In parallel mode, findFirst requires thread coordination — waiting for the “leftmost” result

Optional Overhead

Optional is a heap object. In extremely loaded loops (billions of iterations), creating millions of Optional objects puts pressure on GC. In Java 21+, this is partially solved by JIT optimizations.

Edge Cases

Infinite Streams: Both methods work safely with infinite streams (Stream.generate(...)).

Null handling: If findFirst selects a null element → NullPointerException (Optional cannot contain null).

Diagnostics

Non-determinism Testing: findAny() in a parallel stream → tests must not depend on a specific object (otherwise they will be “flaky”).

IntelliJ Debugger: Stream Debugger shows how the remaining elements are grayed out (not traversed) after the terminal operation “closes”.


🎯 Interview Cheat Sheet

Must know:

  • Both return Optional<T> — you must handle the empty stream case
  • findFirst() — guarantees the first element in order (Encounter Order)
  • findAny() — can return any matching element (optimization for parallelStream)
  • In a sequential stream they work the same, the difference is in parallel mode
  • findFirst() in parallelStream is expensive — requires thread coordination
  • findAny() is preferable for high-load if the specific instance does not matter
  • If you only need to check existence — anyMatch() is more efficient (no Optional overhead)
  • findAny() in tests — must not depend on a specific object (otherwise flaky tests)

Frequent follow-up questions:

  • When to choose findAny over findFirst? — In parallelStream, when any matching element will do (free worker, active session) — removes synchronization.
  • Why is findFirst expensive in parallelStream? — Threads must coordinate to return the element from the “leftmost” chunk — waiting slows execution.
  • What to do if findFirst returns an empty Optional? — Use orElse(default), orElseGet(supplier), or orElseThrow() depending on business logic.
  • How does findFirst differ from limit(1)? — findFirst returns an Optional of a single element; limit(1) returns a stream (you can continue the pipeline).

Red flags (DO NOT say):

  • “findAny always returns a random element” — incorrect, it returns any available, not necessarily random
  • “findFirst and findAny are the same in parallelStream” — incorrect, findFirst requires thread coordination
  • “You can call .get() without checking” — incorrect, an empty Optional will throw NoSuchElementException
  • “findAny is deterministic in tests” — incorrect, in parallelStream the result is unpredictable

Related topics:

  • [[24. How does short-circuiting work in Stream]]
  • [[25. What are anyMatch(), allMatch(), noneMatch() operations]]
  • [[29. How to work with Optional in Stream]]
  • [[22. When does Stream operation execution begin]]