Stream API practice questions

From OCP Java SE 25 (1Z0-831) · 18 questions on this topic

Stream API practice questions from OCP Java SE 25 (1Z0-831). This pack has 18 questions tagged Stream API, drawn from its timed mock exams. 8 of them are worked through in full below — the question, every option, why each is right or wrong, and the explanation.

Worked examples for Stream API

  1. Question 1

    What is printed? ```java import java.util.stream.*; public class Main { public static void main(String[] args) { System.out.println(Stream.of("fig", "plum", "date") .filter(s -> s.length() == 4) .findFirst()); } } ```

    1. A. Optional[fig]

      Applies findFirst to the source rather than the filtered stream; fig has length 3, is dropped by the length == 4 filter, and never reaches the terminal operation.

    2. B. plum

      Assumes findFirst returns the element itself, or that println unwraps an Optional; getting the bare value needs .get(), .orElseThrow() or .orElse(...).

    3. C. Optional[plum]Correct answer

      filter keeps plum and date (both length 4) and drops fig; findFirst returns the first survivor wrapped in an Optional without unwrapping it, so println calls Optional.toString(), printing Optional[plum].

    4. D. Optional[date]

      Assumes findFirst reports the last element still standing; it reports the first and short-circuits as soon as it has one, so plum wins over date.

    Explanation

    Trace: `filter` keeps `plum` and `date`, both of length 4, and drops `fig`. `findFirst` is a short-circuiting terminal operation that returns the first surviving element wrapped in an `Optional` — it does not unwrap it. `System.out.println` therefore calls `Optional.toString()`, whose format is `Optional[value]`, printing `Optional[plum]`. Why the others are wrong: `plum` assumes `findFirst` returns the element itself, or that `println` unwraps an `Optional`. Getting the bare value needs `.get()`, `.orElseThrow()` or `.orElse(...)`. `Optional[fig]` applies `findFirst` to the source rather than to the filtered stream; `fig` has length 3 and never reaches the terminal operation. `Optional[date]` assumes the pipeline runs to completion and `findFirst` reports the last element still standing. It reports the first, and short-circuits as soon as it has one. Exam tip: every `Optional`-returning terminal operation — `findFirst`, `findAny`, `min`, `max`, and the one-argument `reduce` — prints as `Optional[x]` or `Optional.empty`. The reverse trap is the two-argument `reduce(identity, accumulator)`, which returns the bare element type and never an `Optional`.

  2. Question 2

    What does this print? ```java import java.util.stream.*; public class Main { public static void main(String[] args) { int r = Stream.of(1, 2, 3).reduce(10, Integer::sum); System.out.println(r); } } ```

    1. A. 6

      Sums only the elements (1+2+3) and forgets that the identity 10 is the starting accumulator value, not something discarded.

    2. B. 10

      10 would be the result only if the stream were empty; here three elements are folded in on top of the identity.

    3. C. Optional[16]

      Optional[16] is the shape returned by the one-argument reduce(accumulator), which returns Optional because it has no identity to fall back on. The two-arg form always returns a concrete value, never an Optional.

    4. D. 16Correct answer

      The two-argument reduce(identity, accumulator) seeds the fold with 10 and combines each element in (10+1=11, 11+2=13, 13+3=16), returning a plain int, so it prints 16.

    Explanation

    The two-argument reduce seeds the fold with an identity value and then combines every element into that accumulator, returning a plain value of the element type. Because it always has a seed to fall back on, it never returns an Optional and yields the identity itself for an empty stream. The one-argument reduce, by contrast, has no seed and must return an Optional to represent the empty case, so identifying which overload is called reveals the return type before any arithmetic is done.

  3. Question 3

    The three-argument reduce is given a combiner that multiplies. What does this print? ```java import java.util.stream.*; public class Main { public static void main(String[] args) { int r = Stream.of("a", "bb", "ccc") .reduce(1, (acc, s) -> acc + s.length(), (x, y) -> x * y); System.out.println(r); } } ```

    1. A. 6

      Sums just the three lengths and throws the identity away; the identity 1 is the seed of the fold and is genuinely added in (1 + 1 + 2 + 3 = 7).

    2. B. 7Correct answer

      A sequential stream has only one partial result, so the combiner is never invoked; the accumulator folds left from the identity 1: 1+1=2, 2+2=4, 4+3=7.

    3. C. The output varies between runs, because whether the combiner is applied is unspecified.

      Assumes the runtime may parallelise on its own; a stream is sequential unless you call parallel() or parallelStream(), so this run is fully deterministic and never touches the combiner.

    4. D. 24

      Believes the multiplying combiner merges per-element partial results (2, 3, 4); a sequential stream never splits, so there is nothing for the combiner to merge.

    Explanation

    Trace: the stream is sequential, so there is only ever one partial result and the combiner is never invoked. The accumulator alone folds left from the identity: 1 + "a".length() = 2, 2 + "bb".length() = 4, 4 + "ccc".length() = 7. The program prints `7`. The multiplying combiner is dead code here — it exists only so the same reduction could run in parallel, and the fact that it is not associative-compatible with the accumulator would silently corrupt a parallel run. Why the others are wrong: `6` sums just the three lengths and throws the identity away. The identity is the seed of the fold, so it is genuinely added in: 1 + 1 + 2 + 3. `24` believes the combiner multiplies a per-element partial result, computing identity + length for each element (2, 3, 4) and then folding those with the combiner. A sequential stream never splits, so there is nothing for the combiner to merge. `The output varies between runs, because whether the combiner ...` assumes the runtime may decide to parallelise on its own. A stream is sequential unless you ask for `parallel()` or `parallelStream()`, and this one is fully deterministic. Exam tip: in `reduce(identity, accumulator, combiner)` the combiner runs only when the stream is parallel — a sequential run gives you no signal at all that the combiner is wrong. The contract the exam probes: `combiner.apply(u, accumulator.apply(identity, t))` must equal `accumulator.apply(u, t)`, which multiplication here plainly violates.

  4. Question 4

    What does this print? ```java import java.util.stream.*; public class Main { public static void main(String[] args) { String r = Stream.of(1, 2, 3, 4) .collect(Collectors.teeing( Collectors.summingInt(Integer::intValue), Collectors.counting(), (sum, cnt) -> sum + "/" + cnt)); System.out.println(r); } } ```

    1. A. 10

      10 is only the first downstream collector's result (the sum); teeing always merges both results, it never returns just one.

    2. B. 4/10

      Swaps the merger arguments; the first BiFunction parameter binds to the first collector (summingInt = 10), not the second (counting = 4).

    3. C. 10/4Correct answer

      teeing feeds every element to both collectors: summingInt yields 10 and counting yields 4, then the merger receives them in argument order as (sum, cnt) and builds sum + "/" + cnt, printing 10/4.

    4. D. 2.5

      Assumes the merger divides sum by count to compute an average; the merger here concatenates with a literal '/' character and does no arithmetic.

    Explanation

    teeing runs two downstream collectors over the same elements in a single pass and then combines their two results with a merger function. The merger's parameters bind in the same order as the collector arguments, so the summingInt result arrives first and the counting result second. The merger must be read literally: here it performs string concatenation around a slash, not division, so the output is the two numbers joined rather than their quotient.

  5. Question 5

    What does this print? ```java import java.util.*; import java.util.stream.*; public class Main { public static void main(String[] args) { Map<Integer, String> m = Stream.of("fox", "wolf", "cat", "deer", "ox") .collect(Collectors.groupingBy(String::length, TreeMap::new, Collectors.mapping(s -> s.substring(0, 1), Collectors.joining(",")))); System.out.println(m); } } ```

    1. A. {2=[ox], 3=[fox, cat], 4=[wolf, deer]}

      Ignores the downstream collector; this is what a plain groupingBy(String::length) gives, where each value is a List of the whole words. The mapping+joining downstream collapses each list into a single comma-joined string of first letters.

    2. B. {2=o, 3=f,c, 4=w,d}Correct answer

      Words are grouped by length, then each group's elements are mapped to their first letter and joined with commas; the TreeMap map factory makes keys iterate in ascending numeric order, giving {2=o, 3=f,c, 4=w,d}.

    3. C. {4=w,d, 3=f,c, 2=o}

      Right values but descending keys; a plain TreeMap orders keys ascending, not reversed.

    4. D. {3=f,c, 2=o, 4=w,d}

      Shows encounter or hash order; supplying TreeMap::new forces sorted-key iteration, so this unsorted order cannot occur.

    Explanation

    The three-argument groupingBy takes a classifier, a map factory, and a downstream collector. The classifier buckets the words by length; the downstream mapping+joining rewrites each bucket's value from a list of words into a single comma-joined string of first letters; and the TreeMap map factory dictates the concrete map type and forces keys to iterate in ascending sorted order. Without a sorted map factory a groupingBy returns a HashMap whose iteration order must never be relied upon.

  6. Question 6

    What does this print? ```java import java.util.stream.*; public class Main { public static void main(String[] args) { long c = Stream.of("a", "b", "c") .peek(System.out::print) .count(); System.out.println("=" + c); } } ```

    1. A. abc=3

      Assumes peek always fires. Because no count-affecting operation (like filter) sits in the pipeline, count() takes its shortcut and peek is skipped, so nothing is printed by peek.

    2. B. =0

      Misreads the shortcut: count() still reports the true source size (3), it just avoids walking the elements. Skipping the traversal does not make the count zero.

    3. C. =3Correct answer

      peek does not change the element count, so count() on a SIZED three-element source computes the size directly and skips executing the pipeline; peek never runs and count returns 3.

    4. D. abc

      Assumes the elements flow through peek but the count is not printed; the reverse is true - the count line prints and peek is the part that gets skipped.

    Explanation

    count() is permitted to bypass the pipeline entirely when the source size is known and no operation in the chain could change the element count. A peek does not alter the count, so on a SIZED source the traversal is skipped and the side effect inside peek never fires, while count still reports the true size. Inserting a size-changing operation such as filter would disable this shortcut and force the elements through peek. The question to always ask is whether any stage could change the count.

  7. Question 7

    What does this print? ```java import java.util.stream.*; public class Main { public static void main(String[] args) { boolean r = Stream.iterate(1, n -> n + 1) .peek(System.out::print) .anyMatch(n -> n == 3); System.out.println("=" + r); } } ```

    1. A. The program never terminates

      The infinite source does not hang because anyMatch short-circuits; the program would only run forever if the predicate were never satisfied (e.g. n < 0).

    2. B. 12=true

      Stops one element too early; element 3 must itself be pulled and printed for the predicate to see it and match.

    3. C. 1234=true

      Pulls one element too many; once 3 matches, anyMatch returns immediately without ever requesting a fourth element.

    4. D. 123=trueCorrect answer

      anyMatch short-circuits: 1 prints and fails, 2 prints and fails, 3 prints and satisfies n==3, so anyMatch returns true immediately and never pulls a fourth element, giving 123=true.

    Explanation

    anyMatch is a short-circuiting terminal operation: it pulls elements one at a time and stops the instant the predicate is satisfied, which is what makes an infinite iterate source safe here. Each element that is pulled flows through peek and is printed, including the very element that triggers the match, but nothing after it is ever requested. So the elements up to and including the matching one are printed, then the boolean result follows.

  8. Question 8

    What is the output? ```java import java.util.*; import java.util.stream.*; public class Main { public static void main(String[] args) { IntSummaryStatistics st = IntStream.of(2, 4, 9).summaryStatistics(); System.out.println(st.getAverage()); } } ```

    1. A. 5

      A bare 5 would be an int; getAverage() returns a double, so println always shows 5.0 (never a bare 5), even when the mean is a whole number.

    2. B. 5.0Correct answer

      summaryStatistics() folds the ints into count=3, sum=15; getAverage() is defined as sum/count as a double (15/3 = 5.0), and because the return type is double it prints with a decimal point.

    3. C. 15.0

      15.0 is getSum() (the total of the elements), not the average.

    4. D. 9.0

      9.0 is getMax() (the largest element); the maximum is not the mean.

    Explanation

    summaryStatistics() aggregates the ints into an IntSummaryStatistics holding the count, sum, min, and max in a single pass. The average accessor is specified to compute sum divided by count and to return a double, so a whole-number mean still prints with a trailing decimal. Keeping the accessor types straight matters: count is long, sum is long, min and max are int, and only the average is a double.

Practise all 18 Stream API questions

OCP Java SE 25 has the full set, inside timed mock exams that mirror real exam conditions — every question with a worked explanation.

Open OCP Java SE 25

Other topics in this pack