🌊 Streams API · Intermediate

Terminal operations in Java

forEach, count, min/max, anyMatch/allMatch/noneMatch, findFirst/findAny.

🧩 The mysteryRiddle: in an empty room, is it true that *everyone* is wearing a hat? Java has an answer, and allMatch will surprise you.

The order at the end of the belt

A terminal op pulls elements through and ends the pipeline. The families: values (count, min, max), yes/no questions (anyMatch, allMatch, noneMatch), finders (findFirst, findAny) and actions (forEach).

Three yes/no questions

**anyMatch(p): does at least one pass? allMatch(p): do all pass? noneMatch(p)**: does none pass? They return boolean and short-circuit: they stop as soon as the answer is known.

List<Integer> xs = List.of(4, 9, 2);
xs.stream().anyMatch(n -> n > 8);  // true
xs.stream().allMatch(n -> n > 1);  // true
xs.stream().noneMatch(n -> n < 3); // false
🔮 Predict it

Match three

What does this print?

List<Integer> xs = List.of(3, 6, 9);
boolean a = xs.stream().anyMatch(n -> n > 8);
boolean b = xs.stream().allMatch(n -> n < 5);
boolean c = xs.stream().noneMatch(n -> n < 0);
System.out.println(a + " " + b + " " + c);
  1. true false true
  2. true true true
  3. false false true
Show the answer

true false true — 9 > 8 exists; 6 and 9 aren't < 5, so not all pass; and no number is negative.

🤔 Think first

The empty-room riddle

What does Stream.empty().allMatch(x -> false) return?

Think about it, then reveal the answer

**true! allMatch really asks *"is there an element that breaks the rule?"* — in an empty stream there's none (vacuous truth**). anyMatch on an empty stream is false, and noneMatch is true.

Answers that might not exist

min, max, findFirst and findAny return an **Optional, because the stream might be empty**. Printing it shows Optional[...]; call get() (or safer orElse) to unwrap the value.

🔮 Predict it

Printing a max

What does this print?

var m = Stream.of(5, 2, 8)
    .min(Integer::compare);
System.out.println(m);
System.out.println(m.get());
  1. 2 2
  2. Optional[2] 2
  3. Optional[8] 8
Show the answer

Optional[2] then 2 — min wraps its answer in an Optional; get() unwraps it.

⚠️ The trap

findAny is not 'first'

**findAny() promises only *some* element. In a parallel stream, threads return whichever match they find first, so the answer can change between runs. Need the first in order? Use findFirst()** — in parallel it costs extra coordination.

💼 In the real world

Validation in one line

Real code validates with these daily: items.stream().allMatch(Item::inStock) before checkout. The empty case bites: an empty cart passes allMatch and "everything is in stock" — production bugs have shipped exactly like that. Check for emptiness explicitly when it matters.

Key takeaways

  1. min/max/findFirst return Optional — the stream may be empty
  2. anyMatch/allMatch/noneMatch short-circuit and return boolean
  3. allMatch on an empty stream is true (nothing breaks the rule)
  4. findAny may return any element; useful in parallel streams
🤯 Did you know?

Vacuous truth isn't a Java quirk — it's standard logic. "All unicorns in this room are purple" is technically true when the room has no unicorns.

Practice questions

What does this print?

List<Integer> xs = List.of(4, 9, 2, 7);
boolean any = xs.stream().anyMatch(n -> n > 8);
boolean all = xs.stream().allMatch(n -> n > 1);
boolean none = xs.stream().noneMatch(n -> n < 3);
System.out.println(any + " " + all + " " + none);
  1. true true false
  2. true false false
  3. false true true
  4. true true true
Check your answer

true true false. 9 > 8 so anyMatch is true; every number is > 1 so allMatch is true; 2 < 3 exists, so noneMatch is false.

What does this print?

var s = Stream.of("kiwi", "fig", "banana")
    .max(Comparator.comparing(String::length));
System.out.println(s);
System.out.println(s.get());
  1. banana banana
  2. Optional[banana] banana
  3. Optional[kiwi] kiwi
  4. 6 banana
Check your answer

Optional[banana] banana. max returns an Optional because the stream might be empty. Printing the Optional shows Optional[banana]; get() unwraps the value.

Next: reduce folds a whole stream into one value — and a wrong starting value can quietly add 10 several times.