skip to content

How do anyMatch, allMatch, and noneMatch short-circuit, and what do they return on an empty stream?

level: middleimportance: must knowfreq 58%

answer

  1. any stops at first match → true
  2. all stops at first failure → false
  3. none stops at first match → false
  4. Empty: any=false, all=true, none=true (vacuous truth)
  5. Best case early exit, worst case full scan

basics

~20 s

All three are short-circuiting terminals returning a boolean. anyMatch stops and returns true at the first match; allMatch stops and returns false at the first element that fails; noneMatch stops and returns false at the first match. On an empty stream: anyMatch is false, while allMatch and noneMatch are both true (vacuously).

solid answer

~50 s

anyMatch, allMatch, and noneMatch are short-circuiting terminal operations that each take a predicate and return a boolean. anyMatch returns true and stops at the first element satisfying the predicate. allMatch returns false and stops at the first element that fails it. noneMatch returns false and stops at the first element that satisfies it. Each stops only at its respective counterexample; if no such element exists they must traverse the whole stream — so they are best-case early exit, worst-case full scan. The empty-stream results follow logic's vacuous-truth rule: anyMatch is false (no element to satisfy it), while allMatch and noneMatch are both true (there is no element to violate the all-pass or none-pass claim). This vacuous-true behavior is a common source of bugs — code that assumes allMatch implies a non-empty match will misfire on empty input.

code

java · 11 lines
java
List<Integer> nums = List.of(2, 4, 6, 7, 8);

nums.stream().anyMatch(n -> n % 2 != 0);  // true  — stops at 7
nums.stream().allMatch(n -> n % 2 == 0);  // false — stops at 7
nums.stream().noneMatch(n -> n > 100);    // true  — scans all, no match

// Vacuous truth on an empty stream:
List<Integer> empty = List.of();
empty.stream().anyMatch(n -> true);   // false
empty.stream().allMatch(n -> false);  // true  (!)
empty.stream().noneMatch(n -> true);  // true

go deeper

for a junior

Knows all three return a boolean and that anyMatch stops at the first match.

for a middle

States each operation's deciding element and the empty-stream results (any=false, all=true, none=true), recognizing vacuous truth as a bug source.

for a senior

Explains best-case-early-exit vs worst-case-full-scan, the algebraic equivalences between the three, and why anyMatch beats filter().findFirst().isPresent() for existence checks.

for a principal

Discusses correctness of these checks over empty inputs in domain logic (guarding vacuous-true), and their clean short-circuiting behavior under parallel execution since they're order-independent.

## The three predicate-matchers Each is a **terminal** operation taking a `Predicate<T>` and returning a `boolean`, and each is **short-circuiting**: | Operation | Returns `true` when | Short-circuits (stops early) when it finds | Worst case | |---|---|---|---| | `anyMatch(p)` | at least one element satisfies `p` | the **first** element satisfying `p` (returns `true`) | no match → scans all, returns `false` | | `allMatch(p)` | every element satisfies `p` | the **first** element *failing* `p` (returns `false`) | all pass → scans all, returns `true` | | `noneMatch(p)` | no element satisfies `p` | the **first** element satisfying `p` (returns `false`) | none match → scans all, returns `true` | ## What 'short-circuit' means here Each operation can stop the moment it hits the element that **decides** the answer: - `anyMatch` is decided by the first *match*. - `allMatch` is decided by the first *non-match* (one counterexample disproves 'all'). - `noneMatch` is decided by the first *match* (one example disproves 'none'). If that deciding element never appears, the operation has no choice but to look at **every** element. So all three are *best-case* early exit, *worst-case* full traversal. They still qualify as short-circuiting because they *may* stop early. ## The empty-stream results — vacuous truth This is the part interviewers love. On an **empty** stream (no elements): - `anyMatch(p)` → **false**. 'Does *some* element satisfy p?' — there's no element, so no. - `allMatch(p)` → **true**. 'Do *all* elements satisfy p?' — there are none to violate it, so vacuously yes. - `noneMatch(p)` → **true**. 'Do *no* elements satisfy p?' — there are none, so trivially yes. This mirrors universal quantification in logic: a statement about *all* members of an empty set is vacuously true. The trap: `users.stream().allMatch(User::isVerified)` returns `true` for an empty user list, so code reading that as 'I have at least one verified user' is wrong. Guard with a separate emptiness check when 'all' must imply 'at least one'. ## Relationships between them - `noneMatch(p)` is equivalent to `allMatch(p.negate())` and to `!anyMatch(p)`. - `anyMatch(p)` is equivalent to `!noneMatch(p)`. Prefer the operation that reads most naturally for intent; they short-circuit equivalently. ## Versus find + filter `anyMatch(p)` is the idiomatic boolean existence check — clearer and equally short-circuiting compared to `filter(p).findFirst().isPresent()`. ## Parallel behavior In parallel, all three still short-circuit: once any worker finds the deciding element, the others can cancel. They don't depend on encounter order (a boolean is order-independent), so they parallelize cleanly. ## Takeaway Three boolean short-circuiting terminals, each stopping at its deciding element. Memorize the empty-stream answers — `any=false`, `all=true`, `none=true` (vacuous truth) — because that edge case is both a frequent interview question and a real bug source.

  • users.stream().allMatch(User::isActive) returns true for an empty list. Why, and how would you guard against treating that as 'has active users'?
    It is vacuously true: with no elements, there is no counterexample to make 'all' false. To require at least one active user, add an emptiness check — e.g. !users.isEmpty() && users.stream().allMatch(User::isActive) — or use anyMatch(User::isActive) if you only need existence.
  • Express noneMatch(p) using allMatch.
    noneMatch(p) is equivalent to allMatch(p.negate()) — 'no element satisfies p' means 'every element satisfies not-p'. It is also equal to !anyMatch(p).

saying these in an interview costs you the question

  • Saying allMatch returns false on an empty stream (it returns true — vacuous truth)
  • Saying anyMatch returns true on an empty stream (it returns false)
  • Believing these always short-circuit — they only do so when the deciding element exists
  • Using filter(p).findFirst().isPresent() instead of anyMatch(p)
  • Assuming allMatch == true guarantees at least one element

context