Java Streams interview questions 35 questions in 3 chapters
- 01Coding drills20 questions
- 02Core Stream concepts10 questions
- 03Parallel streams and internals5 questions
Coding drills
Twenty coding drills on grouping, sorting, frequency, and collectors using a shared Employee model.
1.1 Find the second-highest salary Medium 1 min
I map salaries, call distinct so ties at the top do not hide the real second place, sort descending, skip one, and take findFirst. That returns an Optional, which stays empty if there are fewer than two distinct salaries. Sorting is fine for interviews; for large data I would track the top two in one pass instead.
Read the full answer1.2 Find the highest salary in each department Medium 1 min
I group by department and take the max-salary employee per group with groupingBy and maxBy, which wraps each result in Optional. For a plain map I use toMap with a merge that keeps the higher salary. On ties only one employee is kept unless they ask for every tied person.
Read the full answer1.3 Find duplicate elements in a list Medium 1 min
I group elements by identity, count them, and keep keys whose count is greater than one. That stays pure and works with parallel streams. The HashSet add trick is shorter but uses a stateful lambda, so I avoid it unless the stream is clearly sequential. Either way I mention equals and hashCode have to be correct.
Read the full answer1.4 First non-repeated character Medium 1 min
I count characters with groupingBy into a LinkedHashMap so first-occurrence order is kept, then walk the entries and take the first with count one. A plain HashMap would lose order. I also mention chars gives UTF-16 code units, so for emoji I would use codePoints instead.
Read the full answer1.5 Count character frequency in a string Easy 1 min
I turn the string into a stream of characters and collect with groupingBy and counting. That gives a map from character to frequency. Before coding I clarify case folding and whitespace, and whether they care about code points versus UTF-16 chars for non-BMP text.
Read the full answer1.6 Count word frequency in a sentence Easy 1 min
I lower-case the text, split on non-word characters, drop blanks, and group with counting. In the interview I clarify tokenization: punctuation, apostrophes, and Unicode letters. Plain W is ASCII-only unless the Unicode character class flag is on.
Read the full answer1.7 First repeated character Easy 1 min
I walk the character stream and keep a seen set; the first character whose add returns false is the answer. It short-circuits early, which is nice. The lambda is stateful, so this is sequential only. In many interviews a plain loop is equally fine and sometimes clearer.
Read the full answer1.8 Employees earning above average salary Easy 1 min
I compute the average on a DoubleStream first, then open a second stream from the list and filter above that average. One stream cannot be traversed twice, so the two-pass shape is required. For real money I would use BigDecimal, not double.
Read the full answer1.9 Group employees by department Easy 1 min
I collect with groupingBy on the department field, which gives a map from department name to the list of employees. The default map and lists have no type or mutability guarantees. A null department throws, and I can swap in TreeMap or a downstream collector like counting if they ask.
Read the full answer1.10 Sort employees by salary Easy 1 min
I sort with a comparator on salary, usually descending, then thenComparing on name as a tie-break. For ordered streams that sort is stable. If names or salaries can be null I say so and use nullsLast so the ordering stays well defined.
Read the full answer1.11 Find maximum and minimum Easy 1 min
For a simple max I use stream max with natural order. When I also need count, sum, and average, I map to IntStream and call summaryStatistics in one pass. On an empty stream getMax returns Integer.MIN_VALUE, which is a trap worth calling out.
Read the full answer1.12 Top 3 highest salaries Medium 1 min
I take distinct salaries, sort descending, and limit to three. I clarify whether they want the top three salary values or the top three employees. For huge lists a size-three min-heap is better than a full sort, but the stream version is what most interviewers expect first.
Read the full answer1.13 Nth highest salary Medium 1 min
Same pattern as second-highest: distinct salaries, descending sort, skip n minus one, findFirst. I validate that n is at least one because skip rejects negatives, and I return empty Optional when there are fewer than n distinct values.
Read the full answer1.14 Partition numbers into even and odd Easy 1 min
I use partitioningBy with a predicate for evenness. The result is always a map with both true and false keys, even when one side is empty. That is the main difference from groupingBy, which only creates keys that actually appear.
Read the full answer1.15 Salary sum and average Easy 1 min
I use summarizingDouble so sum, average, count, min, and max all come from one pass. Floating-point sums can drift with order or parallelism, so for money I switch to a BigDecimal reduce instead of double.
Read the full answer1.16 Convert a list of employees to a map by id Medium 1 min
I collect with toMap from id to the employee. The two-argument form throws if ids collide, so I pass a merge function when duplicates are possible. Null values also blow up because merge rejects them. If I need insertion order I use the four-argument form with LinkedHashMap.
Read the full answer1.17 Names starting with a given character Easy 1 min
I filter employees whose name is non-null and starts with the prefix, then collect to a list. I ask about case sensitivity and null names up front so we do not argue about edge cases after coding.
Read the full answer1.18 Longest string in a list Easy 1 min
I use max with a comparator on string length, which returns an Optional. On ties max keeps one of them with no promise which; if they want the first longest string I use a reduce that only replaces when the new length is strictly greater.
Read the full answer1.19 Remove duplicates using Streams Easy 1 min
I call distinct and collect to a list. On ordered streams the first occurrence is kept. If uniqueness is by a field like email, I toMap with that key, keep the first value on collision, and use LinkedHashMap to preserve order.
Read the full answer1.20 Common elements between two lists Easy 1 min
I put the second list into a HashSet, then filter the first list with contains and distinct. That is linear expected time instead of nested scans. I mention equals and hashCode again if the element type is a custom class.
Read the full answer
Core Stream concepts
Lazy evaluation, map versus flatMap, reduce versus collect, and the other concepts interviewers drill.
2.1 Stream versus Collection Easy 1 min
A collection stores elements and lets you access them repeatedly. A stream has no storage of its own; it carries elements from a source through a pipeline. Streams are lazy, possibly unbounded, and consumable once. Collections are about managing data; streams are about declaring aggregate computation over that data.
Read the full answer2.2 Intermediate versus terminal operations Easy 1 min
Intermediate operations like filter and map return a new stream and stay lazy. Terminal operations like collect and forEach trigger the traversal and consume the pipeline. Intermediate ops are either stateless or stateful, and short-circuiting ones like findFirst can finish early even on infinite streams.
Read the full answer2.3 map versus flatMap Medium 1 min
map is one-to-one: each element becomes one new element. flatMap is one-to-many: each element becomes a stream, and those streams are flattened into one. A typical example is turning a stream of orders into a stream of all line items. Java 16 also added mapMulti as an imperative alternative.
Read the full answer2.4 filter versus map Easy 1 min
filter keeps or drops elements with a predicate; the type stays the same and the count can only shrink. map transforms every element with a function; the count stays the same and the type may change. They solve different jobs and are often chained together.
Read the full answer2.5 map versus peek Medium 1 min
map transforms each element into a new value. peek runs an action and passes the same element through, and the Javadoc says it exists mainly for debugging. Since Java 9 the pipeline may skip peek when the result can be computed directly, so I never put real business side effects there.
Read the full answer2.6 findFirst versus findAny Easy 1 min
findFirst returns the first element in encounter order when the stream has one. findAny may return any element and is explicitly nondeterministic, which helps parallel performance. Both short-circuit and return Optional, and both throw if the chosen element is null.
Read the full answer2.7 reduce versus collect Medium 1 min
reduce is immutable reduction: you combine values into a new value, which fits sums and max. collect is mutable reduction into a container like a list or StringBuilder, so you avoid allocating a new object every step. Building a list with reduce is the classic anti-pattern — use collect instead.
Read the full answer2.8 groupingBy versus partitioningBy Easy 1 min
groupingBy takes a classifier and builds a map with whatever keys appear. partitioningBy takes a boolean predicate and always returns a map with both true and false keys. Both accept downstream collectors. For unordered parallel work there is also groupingByConcurrent.
Read the full answer2.9 What is lazy evaluation? Medium 1 min
Intermediate operations do nothing until a terminal operation runs. Then elements are pulled through the fused pipeline one at a time, not stage by stage. That is why short-circuiting and infinite streams work, and why a peek alone prints nothing until you add a terminal call.
Read the full answer2.10 Can a Stream be reused? Easy 1 min
No. Once you invoke an intermediate or terminal operation, that stream is consumed. Reusing it throws IllegalStateException saying it has already been operated upon or closed. The fix is to re-stream the collection or wrap stream creation in a Supplier.
Read the full answer
Parallel streams and internals
When parallel helps, how distinct and sorted behave, and how exceptions flow through a pipeline.
3.1 Sequential versus parallel streams Medium 1 min
Sequential runs in the caller thread. Parallel splits the source with a Spliterator and runs on the common ForkJoinPool via parallelStream or parallel. The last parallel or sequential call wins for the whole pipeline. Results match sequential only when lambdas are stateless and non-interfering and the ops are associative.
Read the full answer3.2 When should you avoid parallelStream? Hard 1 min
I avoid it on small data or cheap per-element work where split cost dominates, on poorly splittable sources like LinkedList, and whenever lambdas do blocking IO or touch shared mutable state. Order-sensitive ops get expensive in parallel, and on a busy Spring Boot server the common pool is already shared. Always measure first.
Read the full answer3.3 How does distinct work? Hard 1 min
It uses equals and hashCode. On ordered streams the first occurrence is kept; on unordered streams any may be kept. It is a stateful intermediate operation and gets expensive when you keep ordering in parallel, so unordered can help. Under the hood the sequential path is basically a HashSet of seen elements.
Read the full answer3.4 How does sorted work internally? Hard 1 min
It is a stateful barrier: it buffers every upstream element, sorts them when the upstream ends, then pushes downstream. For ordered streams the sort is stable. Natural-order sorted needs Comparable elements. Putting sorted on an infinite stream never finishes, and limit after sorted does not save you.
Read the full answer3.5 How are exceptions handled in Streams? Hard 2 min
Unchecked exceptions from a lambda propagate out of the terminal operation and abort the pipeline. Checked exceptions cannot be declared on Function or Predicate, so I wrap them as unchecked, map failures into a result type, or leave the stream and use a loop when per-element error handling is the real logic. Close resource streams with try-with-resources.
Read the full answer