Mastering Java 8 Stream API: Complete Interview Guide with Answers
What is Java 8 Stream API?
Java 8 introduced the Stream API to process collections of objects in a functional style. It helps to perform operations like filtering, mapping, collecting, etc., in a more readable and concise way.
Streams are not data structures; they don't store elements. They are wrappers around data sources such as collections and arrays that provide a fluent interface for performing operations.
How is Stream different from a Collection?
Aspect |
Collection |
Stream |
|---|---|---|
Stores Data |
Yes |
No |
Iteration Style |
External Iteration |
Internal Iteration |
Reusable |
Yes |
No (once consumed, can't reuse) |
Evaluation |
Eager |
Lazy (for intermediate ops) |
Is a Stream reusable?
No. Once a stream has been consumed using a terminal operation, it can't be reused. A new stream must be created to apply further processing.
Intermediate vs Terminal Operations
-
Intermediate: Return another Stream (e.g.,
map,filter,sorted)
Operation |
Description |
|---|---|
|
Filter elements |
|
Transform each element |
|
Flatten nested structure |
|
Remove duplicates |
|
Sort elements |
|
Take only N elements |
|
Skip first N elements |
|
Look into elements (debug/logging) |
|
Convert to |
|
Convert to |
|
Convert to |
|
Take elements while condition is true |
|
Drop elements while condition is true |
-
Terminal: Trigger the processing and produce a result or side-effect (e.g.,
collect,forEach,reduce)
Terminal Operation Description forEach()Iterate over each element toArray()Convert stream to array reduce()Reduce stream to a single value collect()Collect elements into List, Set, etc. min()/max()Get smallest/largest element count()Count elements in the stream anyMatch()Check if any element matches condition allMatch()Check if all elements match condition noneMatch()Check if no elements match condition findFirst()Get the first element (if present) findAny()Get any element (non-deterministic)
map() vs flatMap()
-
map() transforms each element of the stream.
-
flatMap() is used to flatten a stream of collections.
List<String> words = Arrays.asList("Hello", "World");
List<String> letters = words.stream()
.flatMap(word -> Arrays.stream(word.split("")))
.collect(Collectors.toList());
filter() in Streams
filter() is used to select elements based on a predicate.
List<String> result = list.stream()
.filter(s -> s.startsWith("A"))
.collect(Collectors.toList());
Stream Creation Methods
-
stream()from Collection -
Stream.of(...) -
Arrays.stream(...) -
Stream.iterate(...) -
Stream.generate(...)
Short-circuiting in Streams
Operations like limit(n), findFirst(), anyMatch() stop processing once their condition is met.
Lazy Evaluation
Intermediate operations are not evaluated until a terminal operation is invoked.
collect() to List and Set
List<String> names = stream.collect(Collectors.toList());
Set<String> uniqueNames = stream.collect(Collectors.toSet());
reduce() Explained
Used to combine elements of a stream into a single result.
int sum = numbers.stream().reduce(0, Integer::sum);
Optional in Streams
Used with terminal operations like findFirst(), max(), etc., to handle the case when no result is found.
Sorting with Streams
list.stream().sorted().collect(Collectors.toList());
Grouping
Map<String, List<Employee>> deptMap = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
Removing Duplicates
list.stream().distinct().collect(Collectors.toList());
Sequential vs Parallel Streams
-
Sequential: processes elements in order.
-
Parallel: splits the task and processes using multiple threads.
list.parallelStream().forEach(System.out::println);
Debugging with peek()
Used for debugging; allows viewing the stream elements without modifying them.
stream.peek(System.out::println).collect(Collectors.toList());
Interview Coding Examples
- Second Highest Salary:
Optional<Integer> secondHighest = salaries.stream()
.distinct()
.sorted(Comparator.reverseOrder())
.skip(1)
.findFirst();
- Character Frequency:
Map<Character, Long> freq = str.chars()
.mapToObj(c -> (char)c)
.collect(Collectors.groupingBy(c -> c, Collectors.counting()));
- Sort Transactions:
transactions.stream()
.filter(t -> t.getYear() == 2022)
.sorted(Comparator.comparing(Transaction::getAmount))
.collect(Collectors.toList());
- Duplicates in List:
Set<String> seen = new HashSet<>();
list.stream()
.filter(s -> !seen.add(s))
.collect(Collectors.toSet());
- List to Uppercase:
list.stream().map(String::toUpperCase).collect(Collectors.toList());
- Flatten List of Lists:
listOfLists.stream().flatMap(List::stream).collect(Collectors.toList());
- Max/Min in Stream:
Optional<Integer> max = list.stream().max(Integer::compareTo);
- Partitioning Even/Odd:
Map<Boolean, List<Integer>> partitioned = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
----------------------------------------------------------------------------------------------------------------------
Java 8 Stream API Interview Questions & Answers
1. What is Stream in Java 8?
A Stream is a sequence of elements that you can process in a functional style. Think of it like a smart pipeline – it lets you filter, map, sort, and collect data effortlessly without writing loops.
2. Stream vs Collection
-
Collection stores data (like a box of fruits).
-
Stream processes data (like a machine processing fruits).
Collections are data containers, while Streams are views on that data to perform computation.
3. Types of Streams
-
Sequential Stream – Processes elements in order.
-
Parallel Stream – Uses multiple threads for better performance on large data sets.
4. How to create a Stream?
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
You can create streams from collections, arrays, or using Stream.of().
5. filter()
Filters elements based on a condition.
list.stream().filter(s -> s.startsWith("a"));
Only keeps elements that start with "a".
6. map()
Transforms each element.
list.stream().map(String::toUpperCase);
Like converting lowercase letters to uppercase.
7. flatMap()
Flattens nested structures.
listOfLists.stream().flatMap(List::stream);
If you have a list of lists, this combines them into one big stream.
8. distinct()
Removes duplicates.
list.stream().distinct();
Like filtering out repeated values.
9. sorted()
Sorts elements.
list.stream().sorted();
Natural or custom sorting.
10. limit() and skip()
-
limit(n)– Get only first n elements. -
skip(n)– Skip first n elements.
11. collect()
Terminal operation to convert Stream to List, Set, Map, etc.
List<String> result = list.stream().collect(Collectors.toList());
12. forEach()
Used for side-effects like printing.
list.stream().forEach(System.out::println);
13. reduce()
Reduces the stream to a single value.
int sum = list.stream().reduce(0, Integer::sum);
Like summing numbers or concatenating strings.
14. min(), max(), count()
list.stream().min(Comparator.naturalOrder());
Finds smallest, largest, or total elements.
15. anyMatch(), allMatch(), noneMatch()
Check if some/all/none elements match a condition.
list.stream().anyMatch(s -> s.startsWith("a"));
16. toArray()
Convert Stream to array.
String[] arr = list.stream().toArray(String[]::new);
17. Stream.of()
Creates stream from given values.
Stream.of("a", "b", "c");
18. Generating infinite streams
Stream.iterate(1, n -> n + 1).limit(10);
Useful in special scenarios.
19. How does lazy evaluation work in Streams?
Streams process elements only when needed. Intermediate operations are not executed until a terminal operation is called.
20. Short-circuiting in Streams
Operations like limit(), findFirst() can end processing early.
21. Parallel Stream vs Stream
Parallel stream uses multiple threads and can improve performance – but not always. Use carefully in performance-heavy operations.
22. What are terminal and intermediate operations?
-
Intermediate:
filter(),map(), etc. – returns a stream. -
Terminal:
collect(),forEach(),reduce()– ends the stream pipeline.
23. How to debug a Stream pipeline?
Use .peek() to look at the elements.
stream.peek(System.out::println)
24. Can we reuse a Stream?
No. Once a stream is consumed, it cannot be reused.
25. When not to use Streams?
-
If you need indexed access.
-
If debugging is hard.
-
When performance is critical and measured.
26. Real-World Use Case
Processing list of orders, filtering out cancelled ones, mapping to their prices, summing total revenue.
orders.stream()
.filter(o -> !o.isCancelled())
.map(Order::getPrice)
.reduce(0, Integer::sum);

