Interview
Functional Programming & Streams — Interview Questions (100+)
Detailed Questions
1. What is a Stream and how does it differ from a Collection?
- Short: A pipeline for processing data; not storage.
- Detailed: A
Collectionstores elements; aStreamdescribes a computation over a source. Streams are lazy, single-use, and can be sequential or parallel. They don't mutate the source. - Example:
list.stream().filter(x->x>0).map(x->x*2).toList();
2. Intermediate vs terminal operations?
- Short: Intermediate are lazy and return a Stream; terminal trigger execution.
- Detailed:
filter/map/sorted/distinct/limitare intermediate (build the pipeline).collect/forEach/reduce/count/findFirstare terminal (consume it). Without a terminal op, nothing runs. - Example:
stream.filter(...)alone does nothing until.toList().
3. What does "lazy evaluation" mean for streams?
- Short: Work happens only when a terminal op runs, element-by-element.
- Detailed: Elements flow through the pipeline one at a time; short-circuiting ops (
limit,findFirst,anyMatch) can stop early without processing the whole source. - Example:
Stream.iterate(1,x->x+1).filter(...).findFirst()stops at the first match.
4. map vs flatMap?
- Short: map: 1→1 transform; flatMap: 1→many, then flatten.
- Detailed:
mapapplies a function producing one element each.flatMapproduces a stream per element and concatenates them—used to flatten nested structures. - Example:
lists.stream().flatMap(List::stream)flattensList<List<T>>.
5. reduce vs collect?
- Short: reduce: immutable fold to one value; collect: mutable reduction into a container.
- Detailed:
reduce(identity, accumulator)combines elements (sum, product).collect(Collector)accumulates into lists/maps/strings efficiently (mutable containers, parallel-safe combiners). - Example:
stream.reduce(0,Integer::sum)vsstream.collect(toList()).
6. What are Collectors and common ones?
- Short: Recipes for
collect: toList, toMap, groupingBy, joining, counting. - Detailed: Collectors build/merge results:
groupingBy(Map of groups),partitioningBy(boolean split),mapping,counting,summingInt,averagingDouble,joining,toUnmodifiableList. - Example:
people.stream().collect(groupingBy(Person::city, counting())).
7. When should you use parallel streams?
- Short: Large, CPU-bound, stateless, easily-splittable data—after measuring.
- Detailed: Parallel streams use the common ForkJoinPool. Good for big data with cheap, independent operations and splittable sources (arrays, ArrayList). Avoid for small data, blocking I/O, stateful/ordered operations, or shared mutable state.
- Example:
list.parallelStream().mapToInt(...).sum()—benchmark vs sequential.
8. What is a functional interface?
- Short: An interface with exactly one abstract method.
- Detailed: Lambdas/method refs target functional interfaces.
@FunctionalInterfaceenforces the single-abstract-method rule. Defaults/statics don't count. - Example:
Runnable,Comparator,Function, customCalculator.
9. Core functional interfaces in java.util.function?
- Short: Supplier, Consumer, Function, Predicate, and bi/unary/operator variants.
- Detailed:
Supplier<T> get,Consumer<T> accept,Function<T,R> apply,Predicate<T> test,BiFunction,UnaryOperator,BinaryOperator, plus primitive specializations (IntFunction,ToIntFunction). - Example:
Predicate<Integer> even = x -> x%2==0;
10. What is the difference between findFirst and findAny?
- Short: findFirst respects order; findAny may be faster in parallel.
- Detailed: In sequential streams they're equivalent. In parallel,
findAnycan return any matching element (less coordination), whilefindFirstmust honor encounter order. - Example:
parallel.filter(...).findAny().
11. Why are streams single-use?
- Short: A stream is consumed by its terminal op.
- Detailed: Reusing a consumed stream throws
IllegalStateException. Create a fresh stream from the source if needed. - Example: Store a
Supplier<Stream<T>>to recreate.
12. How do you avoid side effects in streams?
- Short: Use pure functions and collectors, not external mutation.
- Detailed: Prefer
collect/reduceoverforEachthat mutates shared state—especially in parallel (data races). Stateless, non-interfering lambdas are required for correctness. - Example: Build a list with
toList()rather thanforEach(list::add).
13. Optional best practices?
- Short: Return type for "maybe"; chain map/filter; avoid get().
- Detailed: Don't use Optional for fields/params/collections. Prefer
orElse/orElseGet/orElseThrow/ifPresent. UseflatMapto avoid nested Optionals. - Example:
find(id).map(User::email).orElse("none");
14. What are primitive streams and why use them?
- Short: IntStream/LongStream/DoubleStream avoid boxing.
- Detailed: They provide
sum,average,range,summaryStatisticsand prevent autoboxing overhead. Convert withmapToInt/boxed. - Example:
IntStream.rangeClosed(1,100).sum();
15. teeing and other Java 12+ collectors?
- Short:
teeingcombines two collectors' results. - Detailed:
Collectors.teeing(c1, c2, merger)runs two downstream collectors and merges (e.g. average = sum/count in one pass). - Example: compute min and max together.
Rapid-Fire (Q → A)
- Create stream from list? → list.stream().
- From array? → Arrays.stream(arr).
- From values? → Stream.of(a,b,c).
- Infinite stream? → Stream.iterate / Stream.generate.
- Empty stream? → Stream.empty().
- Range of ints? → IntStream.range / rangeClosed.
- Count elements? → stream.count().
- To list? → stream.toList() (Java 16+).
- To set? → collect(toSet()).
- To map? → collect(toMap(k,v)).
- Join strings? → collect(joining(", ")).
- Sum ints? → mapToInt(...).sum().
- Average? → mapToInt(...).average().
- Max? → max(Comparator) / mapToInt().max().
- Sort? → sorted() / sorted(Comparator).
- Distinct? → distinct().
- Limit? → limit(n).
- Skip? → skip(n).
- Peek? → peek() (debugging).
- Map? → map(fn).
- FlatMap? → flatMap(fn).
- Filter? → filter(predicate).
- Reduce? → reduce(identity, acc).
- anyMatch? → boolean any element matches.
- allMatch? → boolean all match.
- noneMatch? → boolean none match.
- findFirst? → first element (ordered).
- findAny? → any element.
- forEach order? → Unspecified in parallel.
- forEachOrdered? → Respects encounter order.
- Collectors.toList vs toUnmodifiableList? → Mutable vs immutable.
- groupingBy? → Map of grouped lists.
- groupingBy downstream? → counting/mapping/summing.
- partitioningBy? → Map<Boolean,List>.
- counting? → Long count per group.
- summingInt? → Integer sum.
- averagingDouble? → Double average.
- mapping collector? → Transform before collecting.
- reducing collector? → Fold within collect.
- minBy/maxBy? → Optional extreme.
- toMap dup keys? → Throws unless merge fn.
- toMap with supplier? → Choose map impl.
- teeing? → Two collectors merged.
- Stream.concat? → Combine two streams.
- boxed()? → IntStream→Stream
. - mapToObj? → Primitive→object stream.
- asLongStream? → Widen IntStream.
- summaryStatistics? → count/sum/min/max/avg.
- takeWhile? → Prefix while predicate (Java 9).
- dropWhile? → Drop prefix (Java 9).
- iterate with predicate? → Bounded iterate (Java 9).
- ofNullable? → 0/1-element stream (Java 9).
- Parallel stream source? → Common ForkJoinPool.
- Set parallelism? → ForkJoinPool custom or system property.
- Stateful op risk? → Breaks parallel correctness.
- Side-effect risk? → Data races in parallel.
- Is sorted stateful? → Yes (buffers).
- Short-circuit ops? → limit, findFirst, anyMatch.
- Lazy until? → Terminal op.
- Reuse stream? → IllegalStateException.
- Stream of map? → map.entrySet().stream().
- Collect to TreeMap? → toMap(...,TreeMap::new).
- Count by predicate? → filter().count().
- First N? → limit(n).
- Nth element? → skip(n-1).findFirst().
- Flatten nested list? → flatMap(List::stream).
- Unique by field? → collect(toMap(field, x->x,(a,b)->a)).values().
- Sort by multiple keys? → comparing().thenComparing().
- Reverse sort? → Comparator.reverseOrder().
- Null-safe compare? → nullsFirst/nullsLast.
- Map then sum? → mapToInt then sum.
- Convert stream to array? → toArray(Type[]::new).
- Lambda capture rule? → Effectively final variables.
- Method ref types? → static/instance/arbitrary/constructor.
- Function compose? → andThen / compose.
- Predicate combine? → and/or/negate.
- Consumer chain? → andThen.
- Supplier use? → Lazy value / factory.
- UnaryOperator? → Function<T,T>.
- BinaryOperator? → BiFunction<T,T,T>.
- Default method on functional iface? → Allowed.
- Can lambda throw checked? → Only if SAM declares it.
- this in lambda? → Enclosing instance.
- this in anonymous class? → The anonymous instance.
- Capturing vs non-capturing lambda? → Uses outer vars or not.
- Collectors.joining args? → delimiter, prefix, suffix.
- Stream to Optional reduce? → reduce(acc) returns Optional.
- average returns? → OptionalDouble.
- IntStream.sum empty? → 0.
- max empty? → empty Optional.
- Collect to string? → joining.
- groupingByConcurrent? → Concurrent grouping.
- toConcurrentMap? → Parallel-friendly map.
- Why not forEach to build list? → Side effects; use collect.
- Lazy infinite + limit safe? → Yes, short-circuits.
- flatMap to IntStream? → flatMapToInt.
- mapMulti (Java 16)? → 1→many without intermediate stream.
- Stream debugging? → peek().
- When loops over streams? → Hot paths/perf-critical or simple iteration.
- Golden rule? → Keep lambdas pure, stateless, and non-interfering.