Chaturmind
LearnDSASystem DesignInterview PrepDevOpsEngineering GrowthBlog
Start learning
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML
  • DevOps
  • Engineering Growth
  • Java Interview Prep

Company

  • Blog
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Interview Prep: 2–5 Years

Revise the Fresher Tier

  • Revise: Core Java Fundamentals (Fresher Tier)
  • Revise: Collections, Threads, Java 8 & Coding (Fresher Tier)
  • Revise: Spring Core, Spring Boot & Spring MVC (Fresher Tier)
  • Revise: Hibernate, SQL, Microservices, Maven & Git (Fresher Tier)

Intermediate Core Java

  • Intermediate OOP & Language Features — Interview Questions
  • Enums, Generics, Pass-by-Value & Imports — Interview Questions
  • Collections Internals & Scenarios — Interview Questions
  • JVM, Memory & Class Loading — Interview Questions
  • Exception Design & Serialization Edge Cases — Interview Questions

Java 8 Deep Dive & Stream Coding

  • Lambdas, Functional Interfaces & Method References — Interview Questions
  • Default Methods, Backward Compatibility & Optional — Interview Questions
  • Stream API Internals (Part 1) — Interview Questions
  • Stream API Internals (Part 2) — Interview Questions
  • Stream Coding Problems (Level 2, Part 1) — Interview Questions
  • Stream Coding Problems (Level 2, Part 2) — Interview Questions

Concurrency & Multithreading

  • Synchronization, Locks & Deadlocks — Interview Questions
  • Executors, ThreadLocal & Concurrent Collections — Interview Questions

Spring Framework In Depth

  • Bean Lifecycle, Contexts & Circular Dependencies — Interview Questions
  • Spring Bean Conflicts, AOP, Batch & Injection Styles — Interview Questions

Spring Boot In Depth & Scenarios

  • Spring Boot Internals & Auto-Configuration — Interview Questions
  • Spring Boot Configuration, Profiles & Secrets — Interview Questions
  • Spring Boot Data, Transactions & Caching — Interview Questions
  • Spring Boot Testing, Error Pages & Deployment — Interview Questions
  • Spring Boot Performance, Scaling & Resilience — Interview Questions
  • Spring Boot Async, Events & Messaging — Interview Questions
  • Spring Boot Security Scenarios — Interview Questions
  • Actuator, AOP, Spring Cloud & Distributed Tracing — Interview Questions
  • External APIs, Files, GraphQL & WebFlux — Interview Questions

Spring Security

  • Spring Security — OAuth2, CORS, CSRF & Access Rules — Interview Questions
  • Spring Security — Passwords, Filter Chain, Sessions & Debugging — Interview Questions

Apache Kafka

  • Kafka Architecture, Topics & ZooKeeper vs KRaft — Interview Questions
  • Kafka Producers, Consumers, Reliability & Streams — Interview Questions
  • Kafka Security, Connect & Core Scenarios — Interview Questions
  • Kafka Production Scenarios — Interview Questions

Git, Maven & Gradle (Level II)

  • Git Workflows, Rebase & Conflict Handling — Interview Questions
  • Git Recovery, Hooks, Tags & Collaboration — Interview Questions
  • Maven Builds, Multi-Module Projects & Dependency Resolution — Interview Questions
  • Maven Profiles, settings.xml, Plugins & Quality Gates — Interview Questions
  • Gradle Fundamentals, Migration & Dependencies — Interview Questions
  • Gradle Performance, Plugins, Testing & Publishing — Interview Questions

Deployment & CI/CD

  • Sessions, CI/CD Pipelines, Rollbacks & Secrets — Interview Questions
  • Migrations, Zero-Downtime Releases, Containers & Monitoring — Interview Questions

JUnit 5 & Mockito

  • JUnit 5 — Interview Questions
  • Mockito Basics — Interview Questions
  • Mockito Advanced & Tricky Questions — Interview Questions
Chaturmind
← Java Interview Prep: 2–5 Years

Revise the Fresher Tier

  • Revise: Core Java Fundamentals (Fresher Tier)
  • Revise: Collections, Threads, Java 8 & Coding (Fresher Tier)
  • Revise: Spring Core, Spring Boot & Spring MVC (Fresher Tier)
  • Revise: Hibernate, SQL, Microservices, Maven & Git (Fresher Tier)

Intermediate Core Java

  • Intermediate OOP & Language Features — Interview Questions
  • Enums, Generics, Pass-by-Value & Imports — Interview Questions
  • Collections Internals & Scenarios — Interview Questions
  • JVM, Memory & Class Loading — Interview Questions
  • Exception Design & Serialization Edge Cases — Interview Questions

Java 8 Deep Dive & Stream Coding

  • Lambdas, Functional Interfaces & Method References — Interview Questions
  • Default Methods, Backward Compatibility & Optional — Interview Questions
  • Stream API Internals (Part 1) — Interview Questions
  • Stream API Internals (Part 2) — Interview Questions
  • Stream Coding Problems (Level 2, Part 1) — Interview Questions
  • Stream Coding Problems (Level 2, Part 2) — Interview Questions

Concurrency & Multithreading

  • Synchronization, Locks & Deadlocks — Interview Questions
  • Executors, ThreadLocal & Concurrent Collections — Interview Questions

Spring Framework In Depth

  • Bean Lifecycle, Contexts & Circular Dependencies — Interview Questions
  • Spring Bean Conflicts, AOP, Batch & Injection Styles — Interview Questions

Spring Boot In Depth & Scenarios

  • Spring Boot Internals & Auto-Configuration — Interview Questions
  • Spring Boot Configuration, Profiles & Secrets — Interview Questions
  • Spring Boot Data, Transactions & Caching — Interview Questions
  • Spring Boot Testing, Error Pages & Deployment — Interview Questions
  • Spring Boot Performance, Scaling & Resilience — Interview Questions
  • Spring Boot Async, Events & Messaging — Interview Questions
  • Spring Boot Security Scenarios — Interview Questions
  • Actuator, AOP, Spring Cloud & Distributed Tracing — Interview Questions
  • External APIs, Files, GraphQL & WebFlux — Interview Questions

Spring Security

  • Spring Security — OAuth2, CORS, CSRF & Access Rules — Interview Questions
  • Spring Security — Passwords, Filter Chain, Sessions & Debugging — Interview Questions

Apache Kafka

  • Kafka Architecture, Topics & ZooKeeper vs KRaft — Interview Questions
  • Kafka Producers, Consumers, Reliability & Streams — Interview Questions
  • Kafka Security, Connect & Core Scenarios — Interview Questions
  • Kafka Production Scenarios — Interview Questions

Git, Maven & Gradle (Level II)

  • Git Workflows, Rebase & Conflict Handling — Interview Questions
  • Git Recovery, Hooks, Tags & Collaboration — Interview Questions
  • Maven Builds, Multi-Module Projects & Dependency Resolution — Interview Questions
  • Maven Profiles, settings.xml, Plugins & Quality Gates — Interview Questions
  • Gradle Fundamentals, Migration & Dependencies — Interview Questions
  • Gradle Performance, Plugins, Testing & Publishing — Interview Questions

Deployment & CI/CD

  • Sessions, CI/CD Pipelines, Rollbacks & Secrets — Interview Questions
  • Migrations, Zero-Downtime Releases, Containers & Monitoring — Interview Questions

JUnit 5 & Mockito

  • JUnit 5 — Interview Questions
  • Mockito Basics — Interview Questions
  • Mockito Advanced & Tricky Questions — Interview Questions
HomeLearnJava Interview PrepJava Interview Prep: 2–5 YearsJava 8 Deep Dive & Stream Coding
✓ FreeIntermediate· 8 min read

Stream API Internals (Part 2) — Interview Questions

Infinite streams, Function and composition, sorting with streams and custom comparators, how sorted() works internally, reduce and its three forms, filter, Collectors.toList vs toList(), Stream.of, limit vs skip, list-to-map conversion with duplicates, iterate vs generate, and count vs sum vs reduce.

Published September 25, 2026


How to use this lesson

These questions look like API trivia, but each hides a trap: toMap throwing on duplicate keys, reduce with the wrong identity, sorted() buffering the whole stream, Stream.of(array) behaving differently for primitive arrays. Mention the trap, and you stand out.

Q1. How do you create an infinite stream?

Short answer: With Stream.iterate(seed, next) (each element is derived from the previous one), or Stream.generate(supplier) (independent values). Always bound them, with limit, takeWhile or a short-circuiting terminal operation.

Stream.iterate(1, n -> n * 2).limit(10).toList();                         // 1, 2, 4 … 512
Stream.iterate(LocalDate.of(2026, 1, 1), d -> d.isBefore(end), d -> d.plusDays(1))   // Java 9: bounded iterate
      .forEach(this::generateDailyReport);
Stream.generate(UUID::randomUUID).limit(5).toList();
IntStream.iterate(0, i -> i + 3).takeWhile(i -> i < 20).sum();

Q2. What is the Function interface, and how is it used?

Short answer: Function<T, R> represents a transformation, R apply(T t). It's what map takes. It composes: f.andThen(g) means apply f, then g, and f.compose(g) means apply g, then f. Function.identity() returns its input unchanged.

Function<String, String> trim = String::strip;
Function<String, String> normalise = trim.andThen(s -> s.toLowerCase(Locale.ROOT));
Map<String, User> byEmail = users.stream().collect(Collectors.toMap(u -> normalise.apply(u.email()), Function.identity()));

Key points to cover:

  • The variants: BiFunction<T,U,R>, UnaryOperator<T>, and primitive specialisations (ToIntFunction, IntFunction), which avoid boxing.

Q3. How do you sort a collection with streams?

Short answer: stream().sorted() for natural order, or sorted(comparator), then collect. The source isn't modified.

List<Product> cheapestFirst = products.stream()
        .sorted(Comparator.comparing(Product::price).thenComparing(Product::name))
        .toList();
List<String> desc = names.stream().sorted(Comparator.reverseOrder()).toList();

Q4. How do you apply a custom comparator in a stream pipeline?

Short answer: Build the comparator with the Comparator factory methods, and pass it to sorted, max, min or collectors such as maxBy, or to a TreeMap supplier in toMap/groupingBy.

Comparator<Employee> bySeniority = Comparator
        .comparing(Employee::level, Comparator.reverseOrder())
        .thenComparing(Employee::joinDate)
        .thenComparing(Employee::id);                      // tie-breaker for a deterministic order

Optional<Employee> mostSenior = employees.stream().min(bySeniority);
Map<String, Employee> topPerDept = employees.stream().collect(Collectors.toMap(
        Employee::department, e -> e, BinaryOperator.minBy(bySeniority)));

Key points to cover:

  • Comparator.nullsFirst and nullsLast handle null keys. comparingInt and comparingLong avoid boxing.

Q5. How does sorted() work internally, with natural ordering versus a comparator?

Short answer: In both cases, sorted() is a stateful barrier. It buffers every upstream element (into an array or list), sorts the buffer once the upstream is exhausted, and only then pushes elements downstream. With natural ordering it uses Comparator.naturalOrder(), which requires the elements to be Comparable; otherwise you get a ClassCastException at runtime. With a custom comparator, it uses yours. Either way, the sort is TimSort, which is stable for ordered streams.

Key points to cover:

  • Optimisation: if the stream is already known to be SORTED by natural order (for example, from a TreeSet spliterator), sorted() with no comparator is a no-op.
  • sorted() followed by limit(k) still sorts everything. For the top-k of a huge stream, a bounded heap is cheaper.
  • sorted() on an infinite stream never completes.

Q6. What is reduce() used for?

Short answer: reduce folds all the elements into a single value with an associative function. It has three forms:

  1. reduce(identity, accumulator): returns T.
  2. reduce(accumulator): returns Optional<T> (the stream may be empty).
  3. reduce(identity, accumulator, combiner): for reducing to a different type, and for combining partial results in parallel.
BigDecimal total = lines.stream().map(Line::amount).reduce(BigDecimal.ZERO, BigDecimal::add);
Optional<Order> largest = orders.stream().reduce((a, b) -> a.total().compareTo(b.total()) >= 0 ? a : b);
int totalChars = words.stream().reduce(0, (sum, w) -> sum + w.length(), Integer::sum);   // String → int

Common trap: a non-neutral identity, such as reduce(10, Integer::sum). In a parallel stream, the identity is applied once per chunk, so the result changes with the number of chunks. For building collections, use collect: reduce is meant for immutable values.

Q7. How does filter() work?

Short answer: filter(Predicate) is a lazy, stateless intermediate operation. As each element reaches it during the terminal operation's single pass, the predicate is evaluated. Elements that return true are passed downstream, and the rest are dropped immediately. It never buffers, and it preserves encounter order.

Q8. What's special about Collectors.toList()? How does it compare with Stream.toList()?

Short answer: collect(Collectors.toList()) gathers the elements into a new List. In practice that's an ArrayList, but no guarantee is made about its type, mutability or thread-safety. Java 16's stream.toList() is shorter, and returns an unmodifiable list.

Collectors.toList()Stream.toList()Collectors.toUnmodifiableList()
MutableIn practice yes (not guaranteed)NoNo
Nulls allowedYesYesNo (NPE)
Available sinceJava 8Java 16Java 10

Key points to cover:

  • If you need a mutable list, say so explicitly: Collectors.toCollection(ArrayList::new).

Q9. How does Stream.of() work?

Short answer: Stream.of(T... values) creates a sequential, ordered stream from its arguments. Stream.of(single) creates a one-element stream. Stream.ofNullable(x) (Java 9) creates an empty stream for null.

Common trap: Stream.of(intArray) gives a Stream<int[]> with one element, because a primitive array isn't a T[]. Use Arrays.stream(intArray) or IntStream.of(...). For an Integer[], Stream.of(arr) does stream the elements.

Q10. What's the difference between limit() and skip()?

Short answer: limit(n) keeps at most the first n elements, and short-circuits: upstream stops producing after n. skip(n) discards the first n elements, and passes the rest. Together they paginate: skip(page * size).limit(size).

Key points to cover:

  • On ordered parallel streams, both are expensive, because they must respect encounter order. Consider unordered() if the order doesn't matter.
  • For database data, paginate in the query (LIMIT/OFFSET, or keyset pagination), not in a stream over a full result set.

Q11. How do you convert a list to a map with streams?

Short answer: collect(Collectors.toMap(keyMapper, valueMapper)). Know its three traps:

  1. Duplicate keys throw an IllegalStateException. Pass a merge function.
  2. null values throw an NPE (a HashMap.merge limitation).
  3. There's no ordering. Pass a map supplier (LinkedHashMap::new, TreeMap::new).
Map<String, Employee> byEmail = employees.stream()
        .collect(Collectors.toMap(Employee::email, Function.identity()));            // throws on a duplicate email

Map<String, BigDecimal> revenueByCity = orders.stream()
        .collect(Collectors.toMap(Order::city, Order::total, BigDecimal::add, TreeMap::new));   // merge + sorted keys

Map<String, List<Employee>> byDept = employees.stream().collect(Collectors.groupingBy(Employee::department));

Q12. What's the difference between Stream.iterate() and Stream.generate()?

Short answer: iterate(seed, f) produces a sequence where each element depends on the previous one (seed, f(seed), f(f(seed))…), and it's ordered. generate(supplier) calls the supplier independently for each element, and the stream is unordered. Use it for random values, UUIDs or constants.

Key points to cover:

  • iterate parallelises poorly, because each element depends on the one before. Java 9's three-argument iterate(seed, hasNext, next) adds a built-in stop condition, like a for loop.

Q13. What's the difference between count(), sum() and reduce()?

Short answer:

  • count() returns the number of elements, as a long. It's on every stream, and can skip executing the pipeline when the size is known.
  • sum() adds up the elements. It's only on primitive streams (IntStream, LongStream, DoubleStream), with no boxing, and it returns 0 when empty.
  • reduce() is the general fold, for any associative operation (sum, product, max, or merging objects).
long n = orders.stream().filter(Order::isPaid).count();
int units = lines.stream().mapToInt(Line::quantity).sum();
BigDecimal revenue = orders.stream().map(Order::total).reduce(BigDecimal.ZERO, BigDecimal::add);   // BigDecimal: reduce

Key points to cover:

  • IntStream.sum() can overflow silently. Use mapToLong, or Math.addExact in a reduce, for large totals.

Follow-up questions this topic invites — and their answers

Q: Why is collect preferred over reduce for building a list? A: reduce expects immutable values. Using it to add to an ArrayList either copies the list at every step (O(n²)), or mutates shared state (which breaks in parallel). collect is designed for mutable reduction, with a supplier, an accumulator and a combiner.

Q: How do you get a Map<Boolean, List<T>> splitting elements by a condition? A: Collectors.partitioningBy(predicate), optionally with a downstream collector such as counting().

Q: What does takeWhile do differently from filter? A: takeWhile stops at the first element that fails the predicate, which makes it short-circuiting and suited to sorted or infinite streams. filter checks every element.

Q: How do you create a stream from a Map? A: Stream a view: map.entrySet().stream(), map.keySet().stream() or map.values().stream(). Entries give you both the key and the value, for example to sort by value with Map.Entry.comparingByValue().

Previous

Stream API Internals (Part 1) — Interview Questions

Next

Stream Coding Problems (Level 2, Part 1) — Interview Questions

AI Tutor

Lesson: Stream API Internals (Part 2) — Interview Questions

Quick actions

AI responses can be inaccurate. Verify critical information.