Skip to main content

Top 50 Java Stream API Coding Interview Questions (with Answers)

50 Java Stream API coding interview questions with answers: grouping, frequency counts, second highest, flatMap, teeing and Gatherers. Every answer was compiled and run on JDK 25, and the output shown is the real output.

Stream questions in interviews are rarely about whether you know filter and map. They are about the handful of places where a stream looks right and quietly isn’t: a toMap that throws on the first duplicate, a peek that never runs, a stream you try to use twice, a “sorted” map that isn’t sorted. This post walks through 50 questions that come up again and again, grouped by the skill they test. Every answer below was compiled and executed, and the output under each one is copied from that run.

Versions: JDK 25.0.4.1 (Temurin). Gatherers is final since JDK 24 (JEP 485), so Q46 to Q48 need JDK 24 or newer. Everything else runs on JDK 17 or later. All 50 answers live in one file, StreamInterviewQuestions.java. Run it with java StreamInterviewQuestions.java, no build tool needed.

The five traps interviewers are really testing

If you only remember five things, make them these. Each is covered by a question below.

  • Streams are single use. A second terminal operation throws IllegalStateException (Q50).
  • Streams are lazy. Without a terminal operation nothing runs, and findFirst stops early, so peek may see only part of the data (Q49).
  • Collectors.toMap throws on duplicate keys unless you pass a merge function (Q31, Q32).
  • groupingBy gives you a HashMap and its iteration order is not something to rely on. Pass a TreeMap::new or LinkedHashMap::new factory when order matters (Q9 onward).
  • distinct and sorted are stateful. They buffer, so “second highest” via sort is O(n log n) memory-hungry on huge inputs (Q3).
Source list.stream() IntStream.range() Stream.iterate() Intermediate (lazy) filter, map, flatMap distinct, sorted, limit gather(…) Terminal (runs it all) collect, reduce, toList findFirst, anyMatch forEach, count Nothing in the middle box executes until the right-hand box is called.
The shape every answer below follows. Most interview mistakes come from forgetting that the middle part is only a description of work, and the right-hand part is what does it.

The diagram is the whole mental model. A source produces elements, intermediate operations describe what to do to them, and a terminal operation pulls elements through one at a time. That pull model is why findFirst can stop early (Q49) and why a stream object can only be pulled through once (Q50). For the full tour of the API see the Java Streams API complete guide and the Collectors cookbook.

The sample data is a list of numbers [5, 3, 9, 1, 9, 7, 3, 8, 2, 8], ten employees (name, department, salary, age), and the sentence "the quick brown fox jumps over the lazy dog the end". Open a question to see the answer, the real output, and a pointer to the method in the source file. Where order matters I use TreeMap or LinkedHashMap, so the output you see is the output you get.

A. Filtering and basics (Q1 to Q8)

Q1. Square the even numbers in a list

Filter first, then map. Doing the cheap filter before the transformation means fewer elements reach map. Note the duplicates survive because nothing removes them.

NUMS.stream().filter(n -> n % 2 == 0).map(n -> n * n).toList()
Q01 even numbers squared -> [64, 4, 64]

Source: StreamInterviewQuestions.java, q01()

Q2. Get the sum, average and max in one pass

summaryStatistics() on a primitive stream computes count, sum, min, max and average in a single traversal. Calling sum(), average() and max() separately would need three streams, because a stream is single use (see Q50).

var s = NUMS.stream().mapToInt(Integer::intValue).summaryStatistics();
s.getSum() + " / " + s.getAverage() + " / " + s.getMax()
Q02 sum / average / max -> 55 / 5.5 / 9

Source: StreamInterviewQuestions.java, q02()

Q3. Find the second highest number

The classic. The trap is the duplicates: sorting [5, 3, 9, 1, 9, ...] descending and skipping one gives 9 again, so call distinct() first. This is O(n log n) and buffers everything, which is fine for an interview and for small lists. For a huge input, a single loop tracking the top two values is O(n) with no buffering, and a good candidate says so.

NUMS.stream().distinct().sorted(Comparator.reverseOrder()).skip(1).findFirst().get()
Q03 second highest -> 8

Source: StreamInterviewQuestions.java, q03()

Q4. Find the nth highest number

Same pipeline, with skip(n - 1). Here n is 3. If n is larger than the number of distinct values, findFirst() returns an empty Optional and the .get() in these samples would throw; production code should use orElseThrow with a message or return the Optional.

int n = 3;
NUMS.stream().distinct().sorted(Comparator.reverseOrder()).skip(n - 1).findFirst().get()
Q04 3rd highest -> 7

Source: StreamInterviewQuestions.java, q04()

Q5. Find the first non-repeated character in a string

Count into a LinkedHashMap so the map keeps first-seen order, then take the first entry whose count is 1. A plain HashMap would give you a correct count but an arbitrary “first”. For swiss, s appears twice, w and i once, and w comes first.

"swiss".chars().mapToObj(x -> (char) x)
    .collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()))
    .entrySet().stream().filter(e -> e.getValue() == 1).map(Map.Entry::getKey).findFirst().get()
Q05 first non-repeated char in swiss -> w

Source: StreamInterviewQuestions.java, q05()

Q6. Find the duplicate elements

Set.add returns false when the element was already there, so filtering on !seen.add(n) keeps every repeat. The trailing distinct() stops a value that appears three times being reported twice. Be ready for the follow-up: this filter has a side effect on a shared set, so it is fine sequentially but wrong for a parallel stream. The grouping approach in Q9 is the parallel-safe one.

Set<Integer> seen = new HashSet<>();
NUMS.stream().filter(n -> !seen.add(n)).distinct().toList()
Q06 duplicates -> [9, 3, 8]

Source: StreamInterviewQuestions.java, q06()

Q7. Remove duplicates and keep the original order

distinct() on an ordered stream keeps the first occurrence of each value, in encounter order.

NUMS.stream().distinct().toList()
Q07 remove duplicates, keep order -> [5, 3, 9, 1, 7, 8, 2]

Source: StreamInterviewQuestions.java, q07()

Q8. What do allMatch, anyMatch and noneMatch return?

All three short-circuit: anyMatch stops at the first hit, allMatch and noneMatch stop at the first counter-example. The edge case worth knowing: on an empty stream allMatch and noneMatch return true and anyMatch returns false.

NUMS.stream().allMatch(n -> n > 0) + " / " + NUMS.stream().anyMatch(n -> n > 8) + " / "
        + NUMS.stream().noneMatch(n -> n > 9)
Q08 allMatch / anyMatch / noneMatch -> true / true / true

Source: StreamInterviewQuestions.java, q08()

B. Grouping and counting (Q9 to Q20)

This is the group interviewers favour, because groupingBy with a downstream collector is where candidates either look fluent or freeze. The pattern is always groupingBy(classifier, mapFactory, downstream). The map factory is optional, and I include it so the printed order is stable.

Q9. Count how many times each number appears

groupingBy(identity, counting()). The result is a Map<Integer, Long>, and the count is a Long, not an Integer, which trips people who assign it to int.

NUMS.stream().collect(Collectors.groupingBy(Function.identity(), TreeMap::new, Collectors.counting()))
Q09 frequency of each number -> {1=1, 2=1, 3=2, 5=1, 7=1, 8=2, 9=2}

Source: StreamInterviewQuestions.java, q09()

Q10. Count the frequency of each character in a string

String.chars() gives an IntStream, so cast to char while boxing, otherwise your map keys are Integer code points and print as 97, 98.

"banana".chars().mapToObj(c -> (char) c)
    .collect(Collectors.groupingBy(Function.identity(), TreeMap::new, Collectors.counting()))
Q10 character frequency in banana -> {a=3, b=1, n=2}

Source: StreamInterviewQuestions.java, q10()

Q11. Group employee names by department

Without a downstream collector, groupingBy returns whole Employee objects. Use mapping to keep only the field you want.

EMP.stream().collect(Collectors.groupingBy(Employee::dept, TreeMap::new,
        Collectors.mapping(Employee::name, Collectors.toList())))
Q11 names grouped by department -> {Eng=[Asha, Bilal, Chitra], HR=[Farid, Gita], Ops=[Dev, Esha], Sales=[Hari, Indu, Jay]}

Source: StreamInterviewQuestions.java, q11()

Q12. Count employees per department
EMP.stream().collect(Collectors.groupingBy(Employee::dept, TreeMap::new, Collectors.counting()))
Q12 head count per department -> {Eng=3, HR=2, Ops=2, Sales=3}

Source: StreamInterviewQuestions.java, q12()

Q13. Average salary per department

averagingDouble returns a Double per group. There are matching summingDouble, summarizingDouble and the Int/Long variants.

EMP.stream().collect(Collectors.groupingBy(Employee::dept, TreeMap::new,
        Collectors.averagingDouble(Employee::salary)))
Q13 average salary per department -> {Eng=115000.0, HR=67500.0, Ops=76000.0, Sales=80000.0}

Source: StreamInterviewQuestions.java, q13()

Q14. Highest-paid employee in each department

maxBy returns an Optional because a collector cannot know a group is non-empty. collectingAndThen unwraps it. Calling .get() is safe here only because groupingBy never creates an empty group. In Sales, Hari and Indu both earn 90000 and Hari wins: when maxBy ties it keeps the first element it saw.

EMP.stream().collect(Collectors.groupingBy(Employee::dept, TreeMap::new,
        Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingDouble(Employee::salary)),
                o -> o.get().name())))
Q14 highest paid per department -> {Eng=Chitra, HR=Gita, Ops=Esha, Sales=Hari}

Source: StreamInterviewQuestions.java, q14()

Q15. Split employees into two groups by a condition

partitioningBy always returns both keys, false and true, even if one side is empty. groupingBy with a boolean classifier would omit an empty side.

EMP.stream().collect(Collectors.partitioningBy(e -> e.salary() >= 90000,
        Collectors.mapping(Employee::name, Collectors.toList())))
Q15 partition salary >= 90000 -> {false=[Dev, Esha, Farid, Gita, Jay], true=[Asha, Bilal, Chitra, Hari, Indu]}

Source: StreamInterviewQuestions.java, q15()

Q16. Join the names in each department into one string

Nest joining as the downstream of mapping.

EMP.stream().collect(Collectors.groupingBy(Employee::dept, TreeMap::new,
        Collectors.mapping(Employee::name, Collectors.joining("/"))))
Q16 names per department, joined -> {Eng=Asha/Bilal/Chitra, HR=Farid/Gita, Ops=Dev/Esha, Sales=Hari/Indu/Jay}

Source: StreamInterviewQuestions.java, q16()

Q17. Word frequency, sorted by count descending

Counting is Q9 on split(" "). The sorting trap is the generic type: Map.Entry.comparingByValue().reversed() does not compile without the explicit type witness Map.Entry.<String, Long>comparingByValue(), because inference gives up at .reversed(). Add thenComparing on the key so ties come out in a defined order.

wordFreq().entrySet().stream()
    .sorted(Map.Entry.<String, Long>comparingByValue().reversed().thenComparing(Map.Entry.comparingByKey()))
    .map(e -> e.getKey() + "=" + e.getValue()).toList()
Q17 words sorted by count desc, then word -> [the=3, brown=1, dog=1, end=1, fox=1, jumps=1, lazy=1, over=1, quick=1]

Source: StreamInterviewQuestions.java, q17(), wordFreq()

Q18. Top 3 most frequent words

Q17 plus limit(3). Note what the output shows: only the is clearly in the top 3. Positions two and three are a tie among six words, decided purely by the alphabetical tie-break. Say that out loud in an interview, because “top 3” is ambiguous when counts tie.

... .sorted(...same comparator as Q17...).limit(3).map(e -> e.getKey() + "=" + e.getValue()).toList()
Q18 top 3 most frequent words -> [the=3, brown=1, dog=1]

Source: StreamInterviewQuestions.java, q18()

Q19. Group words by their length

The classifier can be any function. A method reference like String::length gives Integer keys.

Arrays.stream(SENTENCE.split(" ")).collect(Collectors.groupingBy(String::length, TreeMap::new, Collectors.toList()))
Q19 words grouped by length -> {3=[the, fox, the, dog, the, end], 4=[over, lazy], 5=[quick, brown, jumps]}

Source: StreamInterviewQuestions.java, q19()

Q20. Group anagrams together

Two words are anagrams when their sorted letters are equal, so use the sorted letters as the grouping key. The key is a throwaway; only the values matter.

Stream.of("listen", "silent", "enlist", "google", "gogole", "cat", "act")
    .collect(Collectors.groupingBy(w -> {
        char[] c = w.toCharArray();
        Arrays.sort(c);
        return new String(c);
    }, TreeMap::new, Collectors.toList()))
Q20 group anagrams -> {act=[cat, act], eggloo=[google, gogole], eilnst=[listen, silent, enlist]}

Source: StreamInterviewQuestions.java, q20()

C. Sorting (Q21 to Q26)

Q21. Sort employees by salary descending, then by name

Chain comparators with thenComparing. Hari and Indu both earn 90000, so the name decides: Hari before Indu.

EMP.stream()
    .sorted(Comparator.comparingDouble(Employee::salary).reversed().thenComparing(Employee::name))
    .map(Employee::name).toList()
Q21 salary desc, then name -> [Chitra, Asha, Bilal, Hari, Indu, Esha, Gita, Dev, Farid, Jay]

Source: StreamInterviewQuestions.java, q21()

Q22. Sort strings by length, then alphabetically
Stream.of("pear", "fig", "apple", "kiwi", "date")
    .sorted(Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder())).toList()
Q22 length, then alphabetical -> [fig, date, kiwi, pear, apple]

Source: StreamInterviewQuestions.java, q22()

Q23. Sort a Map by key and keep it sorted

This is the “sorted map that isn’t sorted” trap. If you collect a sorted stream of entries into a plain toMap, you get a HashMap and the order is lost. The fourth argument, LinkedHashMap::new, preserves it. The third argument (the merge function) is required to reach the fourth.

wordFreq().entrySet().stream().sorted(Map.Entry.comparingByKey()).limit(3)
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a, LinkedHashMap::new))
Q23 map sorted by key into LinkedHashMap (first 3) -> {brown=1, dog=1, end=1}

Source: StreamInterviewQuestions.java, q23()

Q24. Reverse a list with streams

There is no reverse() on a stream. Map the indices backwards. Honestly, Collections.reverse or, on JDK 21+, list.reversed() is the better answer in real code; the stream version is the interview answer.

List<String> list = List.of("a", "b", "c", "d");
int n = list.size();
IntStream.range(0, n).map(i -> n - 1 - i).mapToObj(list::get).toList()
Q24 reverse a list -> [d, c, b, a]

Source: StreamInterviewQuestions.java, q24()

Q25. Sort numbers in descending order

On a Stream<Integer>, pass Comparator.reverseOrder(). An IntStream has no comparator overload; box it first (see Q34).

NUMS.stream().sorted(Comparator.reverseOrder()).toList()
Q25 numbers in reverse order -> [9, 9, 8, 8, 7, 5, 3, 3, 2, 1]

Source: StreamInterviewQuestions.java, q25()

Q26. Top 3 and bottom 3 salaries

Sort descending and limit(3) for the top, sort ascending and limit(3) for the bottom. Two passes over the data, because a stream can only be consumed once.

EMP.stream().map(Employee::salary).sorted(Comparator.reverseOrder()).limit(3).toList()
EMP.stream().map(Employee::salary).sorted().limit(3).toList()
Q26 top 3 / bottom 3 salaries -> [130000.0, 120000.0, 95000.0] / [60000.0, 64000.0, 70000.0]

Source: StreamInterviewQuestions.java, q26()

D. Transforming and collecting (Q27 to Q34)

Q27. Flatten a list of lists

map is one-to-one, flatMap is one-to-many: each element becomes a stream and the streams are concatenated. Using map(List::stream) here would give you a Stream<Stream<Integer>>, the usual mistake.

Stream.of(List.of(1, 2), List.of(3), List.of(4, 5, 6)).flatMap(List::stream).toList()
Q27 flatten a list of lists -> [1, 2, 3, 4, 5, 6]

Source: StreamInterviewQuestions.java, q27()

Q28. Split sentences into a flat list of words
Stream.of("hello world", "java streams").flatMap(s -> Arrays.stream(s.split(" "))).toList()
Q28 words from sentences -> [hello, world, java, streams]

Source: StreamInterviewQuestions.java, q28()

Q29. Find the unique letters across several words

flatMapToInt(String::chars) flattens straight to an IntStream without boxing each character first.

Stream.of("apple", "plum").flatMapToInt(String::chars)
    .mapToObj(c -> (char) c).distinct().sorted().map(String::valueOf).collect(Collectors.joining())
Q29 unique letters across words -> aelmpu

Source: StreamInterviewQuestions.java, q29()

Q30. Join strings with a delimiter, prefix and suffix
Stream.of("a", "b", "c").collect(Collectors.joining(", ", "[", "]"))
Q30 join with prefix and suffix -> [a, b, c]

Source: StreamInterviewQuestions.java, q30()

Q31. Collect to a Map when keys can repeat

Supply a merge function, which decides what happens when two elements map to the same key. Here duplicates are concatenated, but the same hook can keep the first, keep the larger, or sum.

Stream.of("apple", "avocado", "banana", "blueberry", "cherry")
    .collect(Collectors.toMap(w -> w.charAt(0), w -> w, (a, b) -> a + "|" + b, TreeMap::new))
Q31 toMap with a merge function -> {a=apple|avocado, b=banana|blueberry, c=cherry}

Source: StreamInterviewQuestions.java, q31()

Q32. What happens if toMap meets a duplicate key and has no merge function?

It throws IllegalStateException. The message names the key and both values, which is genuinely useful when it happens in production on the 10,000th row.

Stream.of("apple", "avocado").collect(Collectors.toMap(w -> w.charAt(0), w -> w))
Q32 toMap without merge function -> Duplicate key a (attempted merging values apple and avocado)

Source: StreamInterviewQuestions.java, q32()

Q33. Build a cartesian product

A flatMap whose inner function maps another stream. Each outer element becomes a stream of pairs.

Stream.of("A", "B").flatMap(l -> Stream.of(1, 2).map(n -> l + n)).toList()
Q33 cartesian product -> [A1, A2, B1, B2]

Source: StreamInterviewQuestions.java, q33()

Q34. Sort an int[] in descending order

Arrays.stream(int[]) is an IntStream, whose sorted() takes no comparator. Box to Integer, sort with a comparator, and unbox back. Boxing a big array is slow and memory-heavy, so for performance sort ascending and read it backwards.

int[] arr = {3, 1, 2};
Arrays.stream(arr).boxed().sorted(Comparator.reverseOrder()).mapToInt(Integer::intValue).toArray()
Q34 int[] sorted descending -> [3, 2, 1]

Source: StreamInterviewQuestions.java, q34()

E. Numbers, ranges and small algorithms (Q35 to Q42)

Q35. Sum the digits of a number

Subtracting '0' converts the character code to its digit value. Without it you would sum character codes.

String.valueOf(9875).chars().map(c -> c - '0').sum()
Q35 sum of digits of 9875 -> 29

Source: StreamInterviewQuestions.java, q35()

Q36. List the prime numbers up to 30

A number is prime when no divisor from 2 to its square root divides it. noneMatch short-circuits at the first divisor.

IntStream.rangeClosed(2, 30)
    .filter(n -> IntStream.rangeClosed(2, (int) Math.sqrt(n)).noneMatch(d -> n % d == 0)).boxed().toList()
Q36 primes up to 30 -> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

Source: StreamInterviewQuestions.java, q36()

Q37. Compute a factorial with reduce

20! is the largest factorial that fits in a long. Anything bigger silently overflows, so use BigInteger and a three-part reduce: an identity (ONE), and an associative operation.

LongStream.rangeClosed(1, 20).mapToObj(BigInteger::valueOf).reduce(BigInteger.ONE, BigInteger::multiply)
Q37 20! with reduce -> 2432902008176640000

Source: StreamInterviewQuestions.java, q37()

Q38. Generate the first 10 Fibonacci numbers

Use Stream.iterate with a two-element state array, then keep the first slot. limit is essential: iterate with one argument is infinite.

Stream.iterate(new long[] {0, 1}, f -> new long[] {f[1], f[0] + f[1]}).limit(10).map(f -> f[0]).toList()
Q38 first 10 Fibonacci numbers -> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Source: StreamInterviewQuestions.java, q38()

Q39. What is the difference between range and rangeClosed?

range(1, 5) excludes the end, rangeClosed(1, 5) includes it. The off-by-one is the whole question.

IntStream.range(1, 5).boxed().toList() + " / " + IntStream.rangeClosed(1, 5).boxed().toList()
Q39 range vs rangeClosed -> [1, 2, 3, 4] / [1, 2, 3, 4, 5]

Source: StreamInterviewQuestions.java, q39()

Q40. Find the min and max in a single pass

Collectors.teeing (Java 12) feeds every element to two collectors and combines their results. That is the single-pass answer to “I need two results from one stream”.

NUMS.stream().collect(Collectors.teeing(
        Collectors.minBy(Integer::compare), Collectors.maxBy(Integer::compare),
        (mn, mx) -> mn.get() + ".." + mx.get()))
Q40 min and max in one pass -> 1..9

Source: StreamInterviewQuestions.java, q40()

Q41. Find the numbers that start with 1

Convert to a string and test the prefix. A numeric solution (repeated division) is faster, but this is the one interviewers expect.

Stream.of(10, 15, 23, 1, 100, 45, 19).map(String::valueOf).filter(s -> s.startsWith("1")).toList()
Q41 numbers starting with 1 -> [10, 15, 1, 100, 19]

Source: StreamInterviewQuestions.java, q41()

Q42. Check whether a string is a palindrome

Compare each character in the first half with its mirror. allMatch stops at the first mismatch. Case, spaces and punctuation are not handled here, so clarify the requirement before you code.

IntStream.range(0, s.length() / 2).allMatch(i -> s.charAt(i) == s.charAt(s.length() - 1 - i))
Q42 palindrome check -> level=true, stream=false

Source: StreamInterviewQuestions.java, q42(), isPalindrome()

F. Advanced and modern (Q43 to Q50)

Q43. What are takeWhile and dropWhile, and how do they differ from filter?

filter tests every element. takeWhile stops at the first failure and discards the rest, even later elements that would have passed. dropWhile does the opposite: it skips the leading elements that pass and keeps everything from the first failure onwards. In the sample the 4 after the 10 is dropped by takeWhile, and kept by dropWhile. Both are intended for ordered data.

Stream.of(1, 2, 3, 10, 4, 5).takeWhile(n -> n < 5).toList()
Stream.of(1, 2, 3, 10, 4, 5).dropWhile(n -> n < 5).toList()
Q43 takeWhile / dropWhile (n < 5) -> [1, 2, 3] / [10, 4, 5]

Source: StreamInterviewQuestions.java, q43()

Q44. Find the longest string with reduce

The one-argument reduce returns an Optional, because an empty stream has no result. The >= keeps the earlier element on a tie. Here banana and cherry both have 6 letters and banana wins for that reason. max(Comparator.comparingInt(String::length)) is the more idiomatic answer.

Stream.of("kiwi", "banana", "fig", "cherry").reduce((a, b) -> a.length() >= b.length() ? a : b).get()
Q44 longest string with reduce -> banana

Source: StreamInterviewQuestions.java, q44()

Q45. Compute an average with teeing instead of averagingInt

An exercise in composition rather than a better way. It shows you can combine a sum and a count into a result. Cast to double before dividing, or integer division truncates the answer.

NUMS.stream().collect(Collectors.teeing(
        Collectors.summingInt(n -> n), Collectors.counting(), (s, c) -> (double) s / c))
Q45 average with teeing -> 5.5

Source: StreamInterviewQuestions.java, q45()

Q46. Split a stream into batches of 3 (Gatherers)

Gatherers.windowFixed(3) groups elements into lists of three. The last window is allowed to be smaller, so 8 elements give [7, 8] at the end. Before JDK 24 this needed a hand-written collector or a loop. Gatherers are covered in depth in Stream Gatherers (JEP 485).

IntStream.rangeClosed(1, 8).boxed().gather(Gatherers.windowFixed(3)).toList()
Q46 batches of 3 (Gatherers.windowFixed) -> [[1, 2, 3], [4, 5, 6], [7, 8]]

Source: StreamInterviewQuestions.java, q46()

Q47. Compute a moving average of 3 (Gatherers)

windowSliding(3) moves one element at a time, so 5 inputs produce 3 windows. Unlike windowFixed, no partial window is emitted.

Stream.of(10, 20, 30, 40, 50).gather(Gatherers.windowSliding(3))
    .map(w -> w.stream().mapToInt(Integer::intValue).average().getAsDouble()).toList()
Q47 moving average of 3 (Gatherers.windowSliding) -> [20.0, 30.0, 40.0]

Source: StreamInterviewQuestions.java, q47()

Q48. Compute a running total (Gatherers)

Gatherers.scan emits every intermediate accumulation. It does not emit the initial value, which is why the output starts at 1 and not 0.

Stream.of(1, 2, 3, 4, 5).gather(Gatherers.scan(() -> 0, Integer::sum)).toList()
Q48 running total (Gatherers.scan) -> [1, 3, 6, 10, 15]

Source: StreamInterviewQuestions.java, q48()

Q49. Are streams lazy? Prove it

The first pipeline has peek, map, filter and findFirst. It reached b, matched, and stopped, so c was never touched. The second pipeline has no terminal operation, and peek never ran at all. A common bug is a stream built for its side effects with no terminal call: it does nothing. Also, peek is documented for debugging, not for logic you rely on.

Stream.of("a", "b", "c").peek(s -> log.add("peek " + s)).map(String::toUpperCase)
    .filter(s -> s.equals("B")).findFirst();
Stream.of("a", "b", "c").peek(s -> noTerminal.add("peek " + s)).map(String::toUpperCase);
Q49 laziness: with findFirst / with no terminal op -> [peek a, peek b] / []

Source: StreamInterviewQuestions.java, q49()

Q50. What happens if you reuse a stream?

The second terminal operation throws IllegalStateException. The fix is to create a new stream from the source each time, or hold a Supplier<Stream<T>>. Collect to a list if you need to traverse the data several times.

Stream<Integer> s = Stream.of(1, 2, 3);
s.forEach(x -> { });
s.forEach(x -> { });
Q50 reusing a stream -> stream has already been operated upon or closed

Source: StreamInterviewQuestions.java, q50()

How to answer these in a real interview

State the plain-loop alternative when one exists. A candidate who says “I’d use a stream here, but for the second highest on a large array a single pass tracking two values is cheaper” scores better than one who streams everything. Streams are also not free: boxing (Q34), sorting to find a maximum (Q3), and a stateful lambda inside filter (Q6) are all places where the loop is clearer or faster. For the broader interview picture see the Java interview preparation guide and the Top 40 Collections questions.

What was and wasn’t tested: every answer was run once on JDK 25.0.4.1 with the data shown, and the output in each block is the real output. I did not benchmark any of them, so the performance remarks (Q3, Q34, Q41) are the general complexity argument, not measurements. Parallel streams were not tested; Q6 is flagged as unsafe for them from how HashSet behaves, not from a run.

Further reading

No Comments yet!

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.