Java Stream API Coding Interview Questions with Answers (40 Problems, Real Output)
40 Java Stream API coding interview questions with solutions and real JDK 25 output: duplicates, second highest salary, groupingBy, first non-repeated character, toMap gotchas.
New21 min read

These are the Java Stream API coding interview questions that come up again and again: finding duplicates, the second highest salary, grouping employees by department, character frequency, the first non-repeated character, sorting a map by value, and about thirty more. Each one has the problem, an idiomatic stream solution, the real output, and a short explanation of why it works.
The page is for Java developers preparing for coding rounds, from freshers who learned streams as "Java 8 stream interview questions" to experienced developers who need to write a groupingBy with a downstream collector without looking it up. Every solution was compiled and run on JDK 25, and the outputs below are copied from that run, not typed by hand.
All employee problems use one shared dataset, defined once below, so you can paste it into a file and run every answer yourself.
In this guide
- The shared dataset
- Stream concepts interviewers check first (lazy evaluation, map vs flatMap, toList)
- Number and list problems (1 to 12)
- String problems (13 to 22)
- Employee problems with groupingBy and collectors (23 to 36)
- Tricky and advanced stream questions (37 to 40)
- Cheat sheet: the 10 stream idioms
- FAQ
The shared dataset
An Employee record, ten employees, a list of numbers with duplicates, and one word. Two employees share the top salary on purpose, because "second highest salary" is only interesting when there is a tie.
import java.util.*;
import java.util.function.Function;
import java.util.stream.*;
record Employee(int id, String name, String dept, String city, int age, int salary) {}
List<Employee> employees = List.of(
new Employee(1, "Asha", "Engineering", "Pune", 29, 120000),
new Employee(2, "Rahul", "Engineering", "Bengaluru", 34, 150000),
new Employee(3, "Priya", "Engineering", "Pune", 26, 95000),
new Employee(4, "Vikram", "Sales", "Mumbai", 41, 90000),
new Employee(5, "Neha", "Sales", "Pune", 31, 70000),
new Employee(6, "Arjun", "HR", "Mumbai", 38, 65000),
new Employee(7, "Meera", "HR", "Bengaluru", 27, 60000),
new Employee(8, "Karan", "Finance", "Mumbai", 45, 130000),
new Employee(9, "Divya", "Engineering", "Bengaluru", 30, 150000),
new Employee(10, "Sameer", "Finance", "Pune", 36, 85000));
List<Integer> nums = List.of(5, 3, 8, 1, 9, 3, 7, 8, 2, 5);
String text = "programming";Each snippet ends with its output as a comment; where no print statement is shown, the line printed is simply System.out.println(result). The snippets do not use static imports, so you will see Collectors.groupingBy and Comparator.reverseOrder() in full. In an interview, saying "assume static imports" is fine and saves writing time.
Stream concepts interviewers check first
Before the coding problems, most interviewers ask two or three concept questions. These four come up most often.
What is the difference between intermediate and terminal operations?
Intermediate operations such as filter, map and sorted return a new stream and do nothing on their own; they are lazy. A terminal operation such as collect, forEach, count or findFirst starts the pipeline and produces a result. Without a terminal operation, no element is processed.
Elements flow through the pipeline one at a time, not stage by stage. Short-circuiting terminals such as findFirst and anyMatch stop pulling as soon as they have an answer, so later elements are never read. Problem 39 proves this with real output.
Stateless operations (filter, map) handle each element on its own. Stateful operations (sorted, distinct) must remember earlier elements, and sorted must see the whole input before it can pass anything on.
What is the difference between map and flatMap?
map turns each element into exactly one new element. flatMap turns each element into a stream of zero or more elements and joins those streams into one flat stream. Use flatMap when each element contains a collection you want to process as individual items.
List<List<String>> teams = List.of(List.of("Asha", "Rahul"), List.of("Neha"));
System.out.println(teams.stream().map(List::size).toList());
System.out.println(teams.stream().flatMap(List::stream).toList());
// Output:
// [2, 1]
// [Asha, Rahul, Neha]What is the difference between toList() and Collectors.toList()?
Stream.toList(), added in Java 16, returns an unmodifiable list. collect(Collectors.toList()) makes no promise about mutability; today it returns an ArrayList. Use toList() by default, and Collectors.toCollection(ArrayList::new) when you really need to modify the result.
List<String> a = Stream.of("x", "y").toList();
List<String> b = Stream.of("x", "y").collect(Collectors.toList());
b.add("z");
System.out.println("collect(toList()) after add: " + b);
a.add("z"); // throws
// Output:
// collect(toList()) after add: [x, y, z]
// toList().add -> UnsupportedOperationExceptionCan you reuse a Java stream?
No. A stream can be consumed once. Calling a second terminal operation throws IllegalStateException. If you need the data twice, keep the source collection and call stream() again, or use a Supplier<Stream<T>>.
Stream<String> s = Stream.of("a", "b");
s.count();
s.count(); // throws
// Output: stream has already been operated upon or closedNumber and list problems
1. How do you filter even numbers and square them?
Chain filter then map. Filter first so you do not square numbers you will throw away.
List<Integer> evenSquares = nums.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.toList();
// Output: [64, 64, 4]The list contains 8 twice, so 64 appears twice. If the interviewer wants unique values, add distinct().
2. How do you find the sum, average, min and max of a list in one pass?
Convert to an IntStream with mapToInt and call summaryStatistics(). It computes all four values (plus the count) in a single pass over the data.
IntSummaryStatistics stats = nums.stream().mapToInt(Integer::intValue).summaryStatistics();
System.out.println(stats.getSum() + " " + stats.getAverage() + " " + stats.getMin() + " " + stats.getMax());
// Output: 51 5.1 1 9Use mapToInt rather than map to avoid boxing every value. The primitive streams (IntStream, LongStream, DoubleStream) also give you sum() and average() directly.
3. How do you find duplicate elements in a list using streams?
Two common answers. The short one uses a HashSet: add returns false when the element is already there. The cleaner one groups by value, counts, and keeps the keys with a count above one.
Set<Integer> seen = new HashSet<>();
List<Integer> duplicates = nums.stream()
.filter(n -> !seen.add(n)) // add() returns false if already present
.distinct()
.toList();
List<Integer> duplicates2 = nums.stream()
.collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()))
.entrySet().stream()
.filter(e -> e.getValue() > 1)
.map(Map.Entry::getKey)
.toList();
System.out.println(duplicates + " " + duplicates2);
// Output: [3, 8, 5] [5, 3, 8]Notice the different orders: the first lists each duplicate when its second copy appears; the second lists them in order of first appearance. The HashSet version relies on a side effect in a lambda, so it is wrong on a parallel stream. Say that before the interviewer does.
4. How do you remove duplicates from a list while keeping the order?
distinct() removes duplicates using equals and keeps the first occurrence in encounter order.
List<Integer> unique = nums.stream().distinct().toList();
// Output: [5, 3, 8, 1, 9, 7, 2]For your own classes, distinct() is only as good as their equals and hashCode. Records get both for free.
5. How do you find the second highest number in a list using streams?
Remove duplicates, sort in descending order, skip one, take the first. findFirst returns an Optional because the list may have fewer than two distinct values.
Optional<Integer> secondHighest = nums.stream()
.distinct()
.sorted(Comparator.reverseOrder())
.skip(1)
.findFirst();
System.out.println(secondHighest.orElseThrow());
// Output: 8Forgetting distinct() is the classic bug: with values 9, 8, 8 you would still get 8 here, but with 9, 9, 8 you would wrongly get 9.
6. How do you get the top 3 numbers from a list?
Sort descending and limit(3).
List<Integer> top3 = nums.stream()
.sorted(Comparator.reverseOrder())
.limit(3)
.toList();
// Output: [9, 8, 8]Ask whether duplicates count. If the answer is "top 3 distinct values", add distinct() before sorting. For a huge stream and small N, a PriorityQueue of size N is more efficient than a full sort, which is a good follow-up point.
7. How do you partition a list into even and odd numbers?
Collectors.partitioningBy splits a stream into a Map<Boolean, List<T>> using a predicate.
Map<Boolean, List<Integer>> evenOdd = nums.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
// Output: {false=[5, 3, 1, 9, 3, 7, 5], true=[8, 8, 2]}The difference from groupingBy: partitioningBy always returns both keys, true and false, even when one list is empty.
8. How do you flatten a list of lists?
Use flatMap(List::stream). Empty inner lists simply contribute nothing.
List<List<Integer>> nested = List.of(List.of(1, 2), List.of(3, 4, 5), List.of(), List.of(6));
List<Integer> flat = nested.stream()
.flatMap(List::stream)
.toList();
// Output: [1, 2, 3, 4, 5, 6]9. How do you merge two lists, remove duplicates and sort?
Stream.concat joins two streams; then distinct() and sorted().
List<Integer> a = List.of(4, 1, 7);
List<Integer> b = List.of(7, 3, 1, 9);
List<Integer> merged = Stream.concat(a.stream(), b.stream())
.distinct()
.sorted()
.toList();
// Output: [1, 3, 4, 7, 9]For more than two lists, use Stream.of(a, b, c).flatMap(List::stream) instead of nesting concat calls.
10. How do you find all numbers that start with 1?
Convert each number to a string and test the prefix.
List<Integer> startsWithOne = List.of(10, 21, 1, 145, 31, 19).stream()
.filter(n -> String.valueOf(n).startsWith("1"))
.toList();
// Output: [10, 1, 145, 19]11. How do you generate the first 10 Fibonacci numbers with a stream?
Use Stream.iterate with a two-element array as the state, map each state to its first value, and limit the infinite stream.
List<Long> fib = Stream.iterate(new long[] {0, 1}, f -> new long[] {f[1], f[0] + f[1]})
.limit(10)
.map(f -> f[0])
.toList();
// Output: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]Stream.iterate without limit never ends. Since Java 9 there is also a three-argument iterate(seed, hasNext, next) that works like a for loop.
12. How do you multiply all numbers with reduce, and sum the squares from 1 to 5?
reduce combines elements pairwise. The one-argument form returns an Optional because the stream may be empty. For ranges, IntStream.rangeClosed is the cleanest source.
Optional<Integer> product = List.of(1, 2, 3, 4, 5).stream().reduce((x, y) -> x * y);
int sumOfSquares = IntStream.rangeClosed(1, 5).map(n -> n * n).sum();
System.out.println(product.orElse(0) + " " + sumOfSquares);
// Output: 120 55String problems
13. How do you count the occurrences of each character in a string using streams?
Turn the string into a stream of characters with chars(), box each int to a Character, and group by identity with counting(). Pass LinkedHashMap::new to keep the order in which characters first appear.
Map<Character, Long> charFreq = text.chars()
.mapToObj(c -> (char) c)
.collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()));
// Output: {p=1, r=2, o=1, g=2, a=1, m=2, i=1, n=1}chars() returns an IntStream, which is why the mapToObj cast is needed. Without the map factory you get a HashMap with no useful order.
14. How do you find the first non-repeated character in a string?
Count characters into a LinkedHashMap (problem 13), then stream the entries and return the first one with a count of 1. The LinkedHashMap is the whole trick: it remembers insertion order, so "first" means first in the string.
Optional<Character> firstNonRepeated = text.chars()
.mapToObj(c -> (char) c)
.collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting()))
.entrySet().stream()
.filter(e -> e.getValue() == 1)
.map(Map.Entry::getKey)
.findFirst();
System.out.println(firstNonRepeated.orElseThrow());
// Output: pWhat the interviewer is checking: whether you know why a plain
groupingBygives the wrong answer. AHashMaphas no defined order, sofindFirston it returns an arbitrary non-repeated character.
15. How do you find the first repeated character in a string?
Walk the characters in order and return the first one that a Set has already seen. The stream stops at the first match, so this is a single pass.
Set<Character> seenChars = new HashSet<>();
Optional<Character> firstRepeated = text.chars()
.mapToObj(c -> (char) c)
.filter(c -> !seenChars.add(c))
.findFirst();
System.out.println(firstRepeated.orElseThrow());
// Output: rIn "programming", r is the first character whose second copy appears (at index 4), before the second m or g.
16. How do you count word frequency in a sentence?
Split on whitespace, stream the array, and group with counting(). A TreeMap sorts the words alphabetically.
String sentence = "the quick brown fox jumps over the lazy dog the end";
Map<String, Long> wordFreq = Arrays.stream(sentence.split("\\s+"))
.collect(Collectors.groupingBy(Function.identity(), TreeMap::new, Collectors.counting()));
// Output: {brown=1, dog=1, end=1, fox=1, jumps=1, lazy=1, over=1, quick=1, the=3}Real text needs more care: lower-case the words first and strip punctuation, or "The" and "the." count as different words.
17. How do you reverse each word in a sentence?
Split into words, reverse each with StringBuilder, and join them back with a space.
String reversedWords = Arrays.stream("Java streams are lazy".split(" "))
.map(w -> new StringBuilder(w).reverse().toString())
.collect(Collectors.joining(" "));
// Output: avaJ smaerts era yzal18. How do you check whether a string is a palindrome with streams?
Compare the character at each index in the first half with its mirror from the end, using IntStream.range and allMatch. allMatch stops at the first mismatch.
String word = "racecar";
boolean palindrome = IntStream.range(0, word.length() / 2)
.allMatch(i -> word.charAt(i) == word.charAt(word.length() - 1 - i));
// Output: trueIt is fine to say that new StringBuilder(word).reverse().toString().equals(word) is simpler. The stream version shows you know IntStream and short-circuiting.
19. How do you join a list of strings with a delimiter, prefix and suffix?
Collectors.joining(delimiter, prefix, suffix).
String csv = Stream.of("Java", "Spring", "Kafka")
.collect(Collectors.joining(", ", "[", "]"));
// Output: [Java, Spring, Kafka]20. How do you sort strings by length, then alphabetically, and find the longest?
Build a comparator with Comparator.comparingInt(String::length) and chain thenComparing for the tie-break. Use max with the same comparator for the longest string.
List<String> words = List.of("kiwi", "banana", "fig", "apple", "cherry", "date");
List<String> byLength = words.stream()
.sorted(Comparator.comparingInt(String::length).thenComparing(Comparator.naturalOrder()))
.toList();
String longest = words.stream().max(Comparator.comparingInt(String::length)).orElseThrow();
System.out.println(byLength + " longest=" + longest);
// Output: [fig, date, kiwi, apple, banana, cherry] longest=banana"banana" and "cherry" are both six letters. On the JDK, max keeps the first of equal elements, so it returns "banana", but the API documentation does not promise which tied element you get. If the interviewer wants all the longest words, or a predictable winner, find the maximum length first and then filter.
21. How do you check if two strings are anagrams?
Lower-case both, sort their characters, and compare the arrays.
String s1 = "Listen", s2 = "Silent";
boolean anagram = Arrays.equals(
s1.toLowerCase().chars().sorted().toArray(),
s2.toLowerCase().chars().sorted().toArray());
// Output: trueSorting is O(n log n). The O(n) alternative counts characters (problem 13) for each string and compares the maps.
22. How do you group names by their first letter?
Use groupingBy with a lambda classifier that returns the first character.
List<String> names = List.of("Asha", "Rahul", "Anil", "Priya", "Arjun", "Meera");
Map<Character, List<String>> byFirstLetter = names.stream()
.collect(Collectors.groupingBy(n -> n.charAt(0), TreeMap::new, Collectors.toList()));
// Output: {A=[Asha, Anil, Arjun], M=[Meera], P=[Priya], R=[Rahul]}Employee problems with groupingBy and collectors
This is where "stream coding questions for experienced" developers are separated from beginners. Almost every problem is groupingBy plus the right downstream collector. The cheat sheet below shows the six combinations you need, with the real output from our dataset.
We pass TreeMap::new in most answers so the keys come out sorted and the output is easy to read. Without it, groupingBy makes no promise about the map type (today it is a HashMap), so the order you see is not guaranteed.
23. How do you group employees by department?
groupingBy(Employee::dept) alone gives Map<String, List<Employee>>. To get only names, add mapping as the downstream collector.
Map<String, List<String>> namesByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::dept, TreeMap::new,
Collectors.mapping(Employee::name, Collectors.toList())));
// Output: {Engineering=[Asha, Rahul, Priya, Divya], Finance=[Karan, Sameer], HR=[Arjun, Meera], Sales=[Vikram, Neha]}24. How do you count employees in each department?
Use counting() as the downstream collector. The values are Long.
Map<String, Long> countByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::dept, TreeMap::new, Collectors.counting()));
// Output: {Engineering=4, Finance=2, HR=2, Sales=2}25. How do you find the average salary by department?
averagingInt(Employee::salary). It always returns a Double.
Map<String, Double> avgSalaryByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::dept, TreeMap::new,
Collectors.averagingInt(Employee::salary)));
// Output: {Engineering=128750.0, Finance=107500.0, HR=62500.0, Sales=80000.0}26. How do you find the highest paid employee in each department?
Use maxBy with a salary comparator. It produces Optional<Employee>, so wrap it in collectingAndThen to unwrap the optional and pick the name.
Comparator<Employee> bySalary = Comparator.comparingInt(Employee::salary);
Map<String, String> topEarnerByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::dept, TreeMap::new,
Collectors.collectingAndThen(
Collectors.maxBy(bySalary),
e -> e.orElseThrow().name())));
// Output: {Engineering=Rahul, Finance=Karan, HR=Arjun, Sales=Vikram}Rahul and Divya both earn 150,000. The JDK's maxBy keeps the first of equal elements, so Rahul wins because he comes first in the list; the documentation does not guarantee that, so add a tie-breaker such as .thenComparing(Employee::name) if the choice matters. If the interviewer asks for all top earners, group to lists first and then filter each list by its maximum.
27. How do you find the second highest salary using Java 8 streams?
Map to salaries, remove duplicates, sort descending, skip one. The distinct() matters here because the top salary appears twice.
Optional<Integer> secondHighestSalary = employees.stream()
.map(Employee::salary)
.distinct()
.sorted(Comparator.reverseOrder())
.skip(1)
.findFirst();
System.out.println(secondHighestSalary.orElseThrow());
// Output: 130000Without distinct() the answer would be 150,000, the same as the highest. This is the stream version of the SQL "second highest salary" question, and the same DISTINCT trap applies.
28. How do you find the employees with the nth highest salary?
Find the nth distinct salary with skip(n - 1), then filter employees with that salary. Returning a list handles ties.
int n = 3;
List<String> nthHighestPaid = employees.stream()
.map(Employee::salary)
.distinct()
.sorted(Comparator.reverseOrder())
.skip(n - 1)
.findFirst()
.map(sal -> employees.stream().filter(e -> e.salary() == sal).map(Employee::name).toList())
.orElse(List.of());
// Output: [Asha]The distinct salaries in order are 150,000, 130,000, 120,000, so the third highest is Asha's 120,000. If n is larger than the number of distinct salaries, the result is an empty list instead of an exception.
29. How do you sort employees by salary descending, then by name?
Chain comparators: comparingInt(Employee::salary).reversed().thenComparing(Employee::name).
List<String> sortedBySalary = employees.stream()
.sorted(Comparator.comparingInt(Employee::salary).reversed()
.thenComparing(Employee::name))
.map(e -> e.name() + "=" + e.salary())
.toList();
// Output: [Divya=150000, Rahul=150000, Karan=130000, Asha=120000, Priya=95000, Vikram=90000, Sameer=85000, Neha=70000, Arjun=65000, Meera=60000]A common mistake is calling reversed() at the end of the whole chain, which also reverses the name order. Put it right after the part you want reversed.
30. Which department has the most employees?
Count by department, then stream the map entries and take the max by value.
String largestDept = employees.stream()
.collect(Collectors.groupingBy(Employee::dept, Collectors.counting()))
.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElseThrow();
// Output: EngineeringIf two departments tie, the result depends on the HashMap iteration order. Mention it, and sort the keys first if you need a predictable answer.
31. How do you get the total, average, min and max salary in one call?
mapToInt(Employee::salary).summaryStatistics().
IntSummaryStatistics salaryStats = employees.stream()
.mapToInt(Employee::salary)
.summaryStatistics();
// Output: IntSummaryStatistics{count=10, sum=1015000, min=60000, average=101500.000000, max=150000}For a per-department version, use groupingBy(Employee::dept, summarizingInt(Employee::salary)).
32. How many employees earn more than 100,000?
partitioningBy with counting() answers "how many above and how many below" in one pass.
Map<Boolean, Long> above100k = employees.stream()
.collect(Collectors.partitioningBy(e -> e.salary() > 100_000, Collectors.counting()));
// Output: {false=6, true=4}33. Who is the youngest employee in Engineering?
Filter by department, then min with an age comparator.
Optional<Employee> youngestInEngineering = employees.stream()
.filter(e -> e.dept().equals("Engineering"))
.min(Comparator.comparingInt(Employee::age));
System.out.println(youngestInEngineering.map(Employee::name).orElse("none"));
// Output: Priya34. How do you list employees whose name starts with "A", sorted?
List<String> startsWithA = employees.stream()
.map(Employee::name)
.filter(name -> name.startsWith("A"))
.sorted()
.toList();
// Output: [Arjun, Asha]Mapping to the name first means sorted() uses the natural order of String, so no comparator is needed.
35. How do you group employees by city and then by department?
Nest a second groupingBy as the downstream collector of the first. The result is a map of maps.
Map<String, Map<String, Long>> byCityThenDept = employees.stream()
.collect(Collectors.groupingBy(Employee::city, TreeMap::new,
Collectors.groupingBy(Employee::dept, TreeMap::new, Collectors.counting())));
// Output: {Bengaluru={Engineering=2, HR=1}, Mumbai={Finance=1, HR=1, Sales=1}, Pune={Engineering=2, Finance=1, Sales=1}}36. How do you give HR employees a 10% raise without changing the original list?
Records are immutable, so map each matching employee to a new record with the new salary and pass everyone else through unchanged. The result is a new list of all ten employees; the source list does not change.
List<Employee> raised = employees.stream()
.map(e -> e.dept().equals("HR")
? new Employee(e.id(), e.name(), e.dept(), e.city(), e.age(), e.salary() * 110 / 100)
: e)
.toList();
raised.stream()
.filter(e -> e.dept().equals("HR"))
.forEach(e -> System.out.print(e.name() + "=" + e.salary() + " "));
System.out.println("| original Arjun=" + employees.get(5).salary());
// Output: Arjun=71500 Meera=66000 | original Arjun=65000Using forEach to modify objects in place is the answer interviewers want you to avoid. Streams are meant for transforming data into new data. In real code, a small withSalary(int) method on the record keeps the lambda short.
Tricky and advanced stream questions
37. Why does Collectors.toMap throw "Duplicate key", and how do you fix it?
toMap(keyMapper, valueMapper) throws IllegalStateException when two elements produce the same key. Pass a third argument, a merge function, that says how to combine the two values. A fourth argument chooses the map type.
try {
Map<String, String> nameByDept = employees.stream()
.collect(Collectors.toMap(Employee::dept, Employee::name));
System.out.println(nameByDept);
} catch (IllegalStateException ex) {
System.out.println(ex.getMessage());
}
Map<String, String> joinedByDept = employees.stream()
.collect(Collectors.toMap(Employee::dept, Employee::name,
(x, y) -> x + "," + y, TreeMap::new));
System.out.println("fixed: " + joinedByDept);
// Output:
// Duplicate key Engineering (attempted merging values Asha and Rahul)
// fixed: {Engineering=Asha,Rahul,Priya,Divya, Finance=Karan,Sameer, HR=Arjun,Meera, Sales=Vikram,Neha}toMap also throws NullPointerException if any value is null, with or without a merge function (we checked both on JDK 25). If values can be null, collect with groupingBy or a plain loop instead.
38. How do you sort a map by its values in descending order?
Stream the entry set, sort with Map.Entry.comparingByValue() reversed, and collect into a LinkedHashMap so the sorted order survives.
Map<String, Integer> scores = Map.of("Asha", 82, "Rahul", 95, "Priya", 88, "Neha", 74);
LinkedHashMap<String, Integer> byScoreDesc = scores.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(x, y) -> x, LinkedHashMap::new));
// Output: {Rahul=95, Priya=88, Asha=82, Neha=74}Two details trip people up. The explicit type witness Map.Entry.<String, Integer>comparingByValue() is needed because Java cannot infer the types before reversed(). And collecting into a plain toMap without LinkedHashMap::new throws the sorting away.
39. Prove that streams are lazy. What does this print?
Add print statements inside filter and map and finish with findFirst. The output shows elements going through the whole pipeline one at a time, and the stream stopping as soon as it has a result.
Stream.of("one", "two", "three", "four")
.filter(w -> { System.out.print("filter(" + w + ") "); return w.length() > 3; })
.map(w -> { System.out.print("map(" + w + ") "); return w.toUpperCase(); })
.findFirst()
.ifPresent(w -> System.out.println("-> " + w));
// Output: filter(one) filter(two) filter(three) map(three) -> THREE"four" is never read, and map runs exactly once. If you remove findFirst and leave no terminal operation, nothing prints at all.
40. How do you split a stream into batches of 3 (Java 24+)?
Since Java 24, Gatherers.windowFixed(3) groups elements into lists of three, with a shorter last batch. Before gatherers, you needed an index-based loop or a library.
List<List<Integer>> batches = IntStream.rangeClosed(1, 8).boxed()
.gather(Gatherers.windowFixed(3))
.toList();
// Output: [[1, 2, 3], [4, 5, 6], [7, 8]]This is the typical answer for "send records to a bulk API 500 at a time". Gatherers.windowSliding(n) gives overlapping windows, useful for moving averages. Our Java 21 to 25 features interview questions cover gatherers and the other new APIs in more depth.
Bonus: why is reduce with a non-zero identity wrong on a parallel stream?
The first argument to reduce must be a true identity for the operation: 0 for addition, 1 for multiplication. A parallel stream starts each chunk from the identity, so a value such as 10 gets added once per chunk instead of once in total.
List<Integer> values = List.of(1, 2, 3, 4);
System.out.println("sequential: " + values.stream().reduce(10, Integer::sum));
System.out.println("parallel: " + values.parallelStream().reduce(10, Integer::sum));
System.out.println("correct: " + (10 + values.parallelStream().reduce(0, Integer::sum)));
// Output:
// sequential: 20
// parallel: 50
// correct: 20The parallel result printed 50 on our machine; the exact wrong number depends on how the stream is split. The same rule explains why the HashSet trick in problem 3 breaks under parallelStream(): lambdas that change shared state are not safe in parallel. Use parallel streams only for large, CPU-heavy work with no shared mutable state, and measure first.
Cheat sheet: the 10 stream idioms
Most stream coding questions are one of these ten patterns with different data. Learn the idioms and you can assemble the rest during the interview.
- Find duplicates:
filter(n -> !seen.add(n)), orgroupingBy(identity(), counting())and keep counts above 1. - Remove duplicates, keep order:
distinct(). - Second highest:
distinct().sorted(reverseOrder()).skip(1).findFirst(). - Top N:
sorted(reverseOrder()).limit(n). - Frequency count:
groupingBy(identity(), counting()). - First non-repeated: the same, with
LinkedHashMap::new, then the first entry with count 1. - Flatten nested lists:
flatMap(List::stream). - Sort a map by value:
entrySet().stream().sorted(comparingByValue()), collected into aLinkedHashMap. - Split even and odd:
partitioningBy(n -> n % 2 == 0). - Join strings:
collect(joining(", ", "[", "]")).
Practise with the full question bank
If you want more problems in the same format, these two PDFs are built for it:
- Java Stream API Coding: 75 Interview Questions with Output: 75 Stream API coding problems solved idiomatically, covering filtering, mapping, sorting, collectors,
groupingBy, the employee dataset and gatherers. - Top 100 Java Coding Interview Programs with Solutions: the 100 coding problems asked most in Java interviews, each with the optimal approach, complexity, edge cases and verified Java code.
Related reading
- Java programs asked in interviews, with output
- Java 21 to 25 features interview questions
- Core Java interview questions
The official java.util.stream package documentation is worth one careful read; its sections on laziness, statelessness and reduction explain most of the gotchas above.
FAQ
Are Java 8 stream interview questions still asked in 2026?
Yes. The Stream API was added in Java 8, but it remains the main way to write collection code in modern Java, and coding rounds still use it heavily. Newer additions such as toList() (Java 16) and gatherers (Java 24) show up as follow-up questions.
Should I use streams or loops in a coding interview?
Use streams when the problem is a transformation (filter, map, group, count) and a loop when you need indexes, early exits with complex state, or mutation. Interviewers mostly want correct, readable code; many will ask you to rewrite a loop as a stream, or the other way round.
What is the difference between findFirst and findAny?
findFirst returns the first element in encounter order. findAny may return any element, which lets a parallel stream return whichever it finds first. On a sequential stream they usually behave the same, but only findFirst guarantees it.
What is the difference between groupingBy and partitioningBy?
partitioningBy takes a predicate and always returns a map with exactly two keys, true and false. groupingBy takes a classifier function and returns one key per distinct result, with no empty groups.
Are parallel streams faster?
Only for large data sets with CPU-heavy, independent work per element. For small lists, I/O, or operations that depend on order, they are often slower and can be wrong if lambdas share mutable state. They also run on the common fork-join pool, which other parts of the application share.




