Back to Stream: reduce & Aggregate
Q3Question 3 of 9

findFirst() vs findAny(): encounter order vs parallel performance

Key Concepts
  • Both are short-circuiting terminal operations that return Optional<T>.
  • In sequential streams, both return the first element and behave identically.
  • In parallel streams, findFirst guarantees the first element in encounter order (deterministic, coordination cost).
  • In parallel streams, findAny returns whichever element any thread finds first (non-deterministic, faster).
  • Use findFirst for deterministic results: audit reports, test assertions, pagination.
  • Use findAny when any match is acceptable: existence checks, dashboard alerts, performance-critical searches.
  • For existence checks, prefer anyMatch(predicate) over findAny().isPresent() for clarity.

Definition

findFirst returns the deterministic first element in order (parallel: coordination cost); findAny returns any match non-deterministically (parallel: fastest thread wins); both are identical in sequential.
  • findFirst(): returns the first element in encounter order. Deterministic. In parallel, threads coordinate to verify no earlier match exists.
  • findAny(): returns any matching element. Non-deterministic in parallel. Whichever thread finds a match first wins.
  • Sequential: both return the first element (identical behaviour).
  • Short-circuit: both stop the pipeline as soon as a result is found.
  • Return type: Optional (empty if stream is empty).

Eight clerks at National Bank of India search filing cabinets for a fraud transaction. findFirst: find the first fraud in cabinet order. Even if Clerk H finds one first, everyone waits to confirm nothing earlier exists. findAny: find any fraud, the first clerk to find one raises their hand immediately. findAny is faster because no coordination is needed.

Understanding the Concepts

What is the difference between findFirst and findAny?

Both are short-circuiting terminal operations that return Optional. They differ in ordering guarantees in parallel streams.

How do they behave differently?

JAVA
package com.bank.core;

import java.util.List;
import java.util.Optional;

public class FindFirstVsFindAny {
    public static void main(String[] args) {
        List<String> accounts = List.of("SAV2001", "SAV2002", "CUR3001", "SAV2003");

        // Sequential: both return SAV2001
        Optional<String> first = accounts.stream().findFirst();
        Optional<String> any = accounts.stream().findAny();
        System.out.println("Sequential first: " + first.orElse("none"));
        System.out.println("Sequential any: " + any.orElse("none"));

        // Parallel: findFirst still returns SAV2001, findAny varies
        Optional<String> firstP = accounts.parallelStream().findFirst();
        Optional<String> anyP = accounts.parallelStream().findAny();
        System.out.println("Parallel first: " + firstP.orElse("none"));
        System.out.println("Parallel any: " + anyP.orElse("none"));
    }
}
TEXT
Sequential first: SAV2001
Sequential any: SAV2001
Parallel first: SAV2001
Parallel any: CUR3001

Comparison table

| Aspect | findFirst | findAny |
|--------|-----------|--------|
| Sequential | First element | First element |
| Parallel | First in order (deterministic) | Any thread's first (varies) |
| Coordination | Yes (threads wait) | No (fastest wins) |
| Use case | Audit, pagination, tests | Existence check, alerts |

When should you use findFirst?

Use findFirst when you need a deterministic, reproducible result: audit reports, test assertions, pagination, or any case where the specific element matters.

JAVA
// Deterministic: always returns the first matching account
Optional<Account> first = accounts.parallelStream()
    .filter(a -> a.balance().compareTo(threshold) > 0)
    .findFirst(); // guaranteed first in encounter order

When should you use findAny?

Use findAny when any match is acceptable: existence checks, dashboard alerts, or performance-critical searches where the fastest answer wins.

JAVA
// Existence check: any fraud transaction will do
Optional<Transaction> fraud = transactions.parallelStream()
    .filter(t -> t.isFlagged())
    .findAny(); // fastest thread wins, no coordination

fraud.ifPresent(t -> alertService.sendFraudAlert(t));

What about empty streams?

Both return Optional.empty() for empty streams or when no element matches the filter.

JAVA
Optional<String> result = List.<String>of().stream().findFirst();
System.out.println(result.isPresent()); // false

Optional<String> result2 = accounts.stream()
    .filter(a -> a.startsWith("XYZ"))
    .findAny();
System.out.println(result2.isPresent()); // false

How do findFirst/findAny compare to anyMatch?

For the common pattern 'check if at least one matches,' prefer anyMatch(predicate) over findAny().isPresent() for clarity.

JAVA
// VERBOSE: findAny + isPresent
boolean exists = accounts.stream()
    .filter(a -> a.startsWith("SAV"))
    .findAny()
    .isPresent();

// CLEANER: anyMatch
boolean exists = accounts.stream()
    .anyMatch(a -> a.startsWith("SAV"));

What are common mistakes?

JAVA
// WRONG: using findAny when deterministic result is needed
Optional<Account> first = accounts.parallelStream()
    .filter(Account::active)
    .findAny(); // non-deterministic in parallel!

// CORRECT: use findFirst for deterministic result
Optional<Account> first = accounts.parallelStream()
    .filter(Account::active)
    .findFirst(); // always the first active account

// WRONG: using findFirst when any match will do (unnecessary coordination)
boolean hasFraud = transactions.parallelStream()
    .filter(Transaction::isFlagged)
    .findFirst().isPresent(); // coordination overhead!

// CORRECT: use anyMatch or findAny
boolean hasFraud = transactions.parallelStream().anyMatch(Transaction::isFlagged);

What should a senior engineer know?

  • findFirst in parallel requires threads to coordinate to verify no earlier match exists, adding overhead.
  • findAny in parallel returns the first result from any thread, requiring no coordination.
  • Both are short-circuiting: they stop the pipeline as soon as a result is found.
  • For existence checks, prefer anyMatch(predicate) over findAny().isPresent() for clarity and intent.
  • On unordered sources (HashSet), findFirst and findAny may behave identically because there is no defined encounter order.

Visual Overview

DIAGRAM
  findFirst (parallel):          findAny (parallel):
  ┌────────────────────┐         ┌────────────────────┐
  │ Thread-1: [1-250]  │         │ Thread-1: [1-250]  │
  │ Thread-2: [251-500]│         │ Thread-2: [251-500]│
  │ Thread-3: [501-750]│ finds   │ Thread-3: [501-750]│ finds
  │                    │ 601     │                    │ 601
  │ Must check: is 601 │         │ Returns 601        │
  │ the FIRST match?   │ slow    │ immediately!       │ fast
  └────────────────────┘         └────────────────────┘

Key Takeaway

findFirst and findAny both return Optional and short-circuit. In sequential streams, both return the first element. In parallel, findFirst guarantees encounter order (slower due to coordination), findAny returns any match (faster, non-deterministic).

Use findFirst for deterministic results. Use findAny for existence checks and performance-critical parallel searches.

Remember one sentence: findFirst is deterministic (first in order), findAny is non-deterministic but faster in parallel.

---

Share
Use ← → arrow keys to navigate