- Stream Basics (Foundation)
- Creating Streams
- Intermediate Operations
- Terminal Operations
- Collectors (Very Important 🚀)
- Reduction & Aggregation Patterns
- FlatMap Patterns (Interview Favorite)
- Optional + Streams
- Parallel Streams
- Primitive Streams
- Stream Ordering & Characteristics
- Error Handling in Streams
- Performance & Best Practices
- Common Interview Stream Problems
- Advanced / Lesser-Known Topics
A Stream is a sequence of elements supporting sequential and parallel aggregate operations. It's not a data structure but a pipeline for processing data.
Key Characteristics:
- No storage: Streams don't store elements
- Functional in nature: Operations produce results without modifying the source
- Lazy evaluation: Intermediate operations are not executed until terminal operation is invoked
- Possibly unbounded: Streams can be infinite
| Collection | Stream |
|---|---|
| Stores data | Processes data |
| Can be traversed multiple times | One-time consumption |
| External iteration (for loop) | Internal iteration |
| Eager evaluation | Lazy evaluation |
Problem 1: Convert Collection to Stream
public List<String> convertToUpperCase(List<String> names) {
return names.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
}Intermediate operations are lazy - they don't execute until a terminal operation is called.
Problem 2: Demonstrate Lazy Evaluation
public void demonstrateLazyEvaluation() {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Stream<String> stream = names.stream()
.filter(name -> {
System.out.println("Filtering: " + name);
return name.length() > 3;
})
.map(name -> {
System.out.println("Mapping: " + name);
return name.toUpperCase();
});
// Nothing printed yet - lazy evaluation
System.out.println("Stream created");
// Terminal operation triggers execution
List<String> result = stream.collect(Collectors.toList());
}Source → Intermediate Operations → Terminal Operation
Problem 3: Complete Stream Pipeline
public int sumOfEvenNumbers(List<Integer> numbers) {
return numbers.stream() // Source
.filter(n -> n % 2 == 0) // Intermediate
.mapToInt(Integer::intValue) // Intermediate
.sum(); // Terminal
}Problem 4: Demonstrate Stream Consumption
public void demonstrateStreamConsumption() {
Stream<String> stream = Stream.of("A", "B", "C");
stream.forEach(System.out::println);
// This will throw IllegalStateException
// stream.forEach(System.out::println);
}Problem 5: Compare Sequential and Parallel
public long sequentialSum(List<Integer> numbers) {
return numbers.stream()
.mapToLong(Integer::longValue)
.sum();
}
public long parallelSum(List<Integer> numbers) {
return numbers.parallelStream()
.mapToLong(Integer::longValue)
.sum();
}Problem 6: Create Stream from Collection
public List<String> filterLongNames(List<String> names) {
return names.stream()
.filter(name -> name.length() > 5)
.collect(Collectors.toList());
}
public List<String> parallelFilterLongNames(List<String> names) {
return names.parallelStream()
.filter(name -> name.length() > 5)
.collect(Collectors.toList());
}Problem 7: Create Stream from Array
public int sumArray(int[] numbers) {
return Arrays.stream(numbers).sum();
}
public double averageArray(String[] names) {
return Arrays.stream(names)
.mapToInt(String::length)
.average()
.orElse(0.0);
}Problem 8: Create Stream Directly
public List<String> createStreamOf() {
return Stream.of("Java", "Python", "JavaScript", "C++")
.filter(lang -> lang.startsWith("J"))
.collect(Collectors.toList());
}Problem 9: Generate Infinite Stream
public List<Double> generateRandomNumbers(int count) {
return Stream.generate(Math::random)
.limit(count)
.collect(Collectors.toList());
}
public List<String> generateConstant(int count) {
return Stream.generate(() -> "Hello")
.limit(count)
.collect(Collectors.toList());
}Problem 10: Generate Sequence
public List<Integer> generateSequence(int start, int count) {
return Stream.iterate(start, n -> n + 1)
.limit(count)
.collect(Collectors.toList());
}
public List<Integer> generateEvenNumbers(int count) {
return Stream.iterate(0, n -> n + 2)
.limit(count)
.collect(Collectors.toList());
}
// Java 9+ with predicate
public List<Integer> generateUntilCondition(int start, int limit) {
return Stream.iterate(start, n -> n < limit, n -> n + 1)
.collect(Collectors.toList());
}Problem 11: Use Primitive Streams
public int sumRange(int start, int end) {
return IntStream.range(start, end).sum();
}
public int sumRangeClosed(int start, int end) {
return IntStream.rangeClosed(start, end).sum();
}
public List<Long> generateLongSequence(int count) {
return LongStream.range(1, count + 1)
.boxed()
.collect(Collectors.toList());
}Problem 12: Read File Lines
public long countLines(String filePath) throws IOException {
try (Stream<String> lines = Files.lines(Paths.get(filePath))) {
return lines.count();
}
}
public List<String> findLinesContaining(String filePath, String keyword)
throws IOException {
try (Stream<String> lines = Files.lines(Paths.get(filePath))) {
return lines.filter(line -> line.contains(keyword))
.collect(Collectors.toList());
}
}
public List<Path> listAllFiles(String directory) throws IOException {
try (Stream<Path> paths = Files.walk(Paths.get(directory))) {
return paths.filter(Files::isRegularFile)
.collect(Collectors.toList());
}
}Problem 13: Create Stream from Optional
public List<String> optionalToStream(Optional<String> optional) {
return optional.stream()
.collect(Collectors.toList());
}
public List<String> filterOptionals(List<Optional<String>> optionals) {
return optionals.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
}Problem 14: Transform Elements
public List<Integer> getLengths(List<String> strings) {
return strings.stream()
.map(String::length)
.collect(Collectors.toList());
}
public List<String> toUpperCase(List<String> strings) {
return strings.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
}Problem 15: Flatten Nested Structures
public List<String> flattenLists(List<List<String>> nestedLists) {
return nestedLists.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
}
public List<Character> getAllCharacters(List<String> words) {
return words.stream()
.flatMap(word -> word.chars()
.mapToObj(c -> (char) c))
.collect(Collectors.toList());
}Problem 16: Filter Elements
public List<Integer> filterEvenNumbers(List<Integer> numbers) {
return numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
}
public List<String> filterByLength(List<String> strings, int minLength) {
return strings.stream()
.filter(s -> s.length() >= minLength)
.collect(Collectors.toList());
}Problem 17: Debug Stream Pipeline
public List<String> debugStream(List<String> names) {
return names.stream()
.peek(name -> System.out.println("Original: " + name))
.filter(name -> name.length() > 3)
.peek(name -> System.out.println("After filter: " + name))
.map(String::toUpperCase)
.peek(name -> System.out.println("After map: " + name))
.collect(Collectors.toList());
}Problem 18: Remove Duplicates
public List<Integer> removeDuplicates(List<Integer> numbers) {
return numbers.stream()
.distinct()
.collect(Collectors.toList());
}
public List<String> uniqueWords(String sentence) {
return Arrays.stream(sentence.split("\\s+"))
.map(String::toLowerCase)
.distinct()
.collect(Collectors.toList());
}Problem 19: Sort Elements
public List<Integer> sortNumbers(List<Integer> numbers) {
return numbers.stream()
.sorted()
.collect(Collectors.toList());
}
public List<String> sortByLength(List<String> strings) {
return strings.stream()
.sorted(Comparator.comparingInt(String::length))
.collect(Collectors.toList());
}
public List<String> sortReverse(List<String> strings) {
return strings.stream()
.sorted(Comparator.reverseOrder())
.collect(Collectors.toList());
}Problem 20: Limit Stream Size
public List<Integer> getFirstN(List<Integer> numbers, int n) {
return numbers.stream()
.limit(n)
.collect(Collectors.toList());
}
public List<Integer> getFirstNEvenNumbers(int n) {
return Stream.iterate(0, i -> i + 2)
.limit(n)
.collect(Collectors.toList());
}Problem 21: Skip Elements
public List<Integer> skipFirstN(List<Integer> numbers, int n) {
return numbers.stream()
.skip(n)
.collect(Collectors.toList());
}
public List<Integer> getPaginated(List<Integer> numbers, int page, int size) {
return numbers.stream()
.skip((long) page * size)
.limit(size)
.collect(Collectors.toList());
}Problem 22: Reduce to Single Value
public int sum(List<Integer> numbers) {
return numbers.stream()
.reduce(0, Integer::sum);
}
public int product(List<Integer> numbers) {
return numbers.stream()
.reduce(1, (a, b) -> a * b);
}
public Optional<String> concatenate(List<String> strings) {
return strings.stream()
.reduce((a, b) -> a + ", " + b);
}
public int max(List<Integer> numbers) {
return numbers.stream()
.reduce(Integer.MIN_VALUE, Integer::max);
}Problem 23: Collect to Different Structures
public List<String> collectToList(Stream<String> stream) {
return stream.collect(Collectors.toList());
}
public Set<String> collectToSet(Stream<String> stream) {
return stream.collect(Collectors.toSet());
}
public String collectToString(List<String> strings) {
return strings.stream()
.collect(Collectors.joining(", "));
}Problem 24: Count Elements
public long countEvenNumbers(List<Integer> numbers) {
return numbers.stream()
.filter(n -> n % 2 == 0)
.count();
}
public long countLongWords(List<String> words, int minLength) {
return words.stream()
.filter(w -> w.length() >= minLength)
.count();
}Problem 25: Find Min/Max
public Optional<Integer> findMin(List<Integer> numbers) {
return numbers.stream()
.min(Integer::compareTo);
}
public Optional<Integer> findMax(List<Integer> numbers) {
return numbers.stream()
.max(Integer::compareTo);
}
public Optional<String> findLongestString(List<String> strings) {
return strings.stream()
.max(Comparator.comparingInt(String::length));
}Problem 26: Iterate Elements
public void printNumbers(List<Integer> numbers) {
numbers.stream()
.forEach(System.out::println);
}
public void printOrderedParallel(List<Integer> numbers) {
numbers.parallelStream()
.forEachOrdered(System.out::println);
}Problem 27: Matching Operations
public boolean hasEvenNumber(List<Integer> numbers) {
return numbers.stream()
.anyMatch(n -> n % 2 == 0);
}
public boolean allPositive(List<Integer> numbers) {
return numbers.stream()
.allMatch(n -> n > 0);
}
public boolean noNegatives(List<Integer> numbers) {
return numbers.stream()
.noneMatch(n -> n < 0);
}Problem 28: Find Elements
public Optional<Integer> findFirstEven(List<Integer> numbers) {
return numbers.stream()
.filter(n -> n % 2 == 0)
.findFirst();
}
public Optional<Integer> findAnyEven(List<Integer> numbers) {
return numbers.parallelStream()
.filter(n -> n % 2 == 0)
.findAny();
}Problem 29: Basic Collectors
public List<String> collectToList(Stream<String> stream) {
return stream.collect(Collectors.toList());
}
public Set<String> collectToSet(Stream<String> stream) {
return stream.collect(Collectors.toSet());
}
public Map<Integer, String> collectToMap(List<String> strings) {
return strings.stream()
.collect(Collectors.toMap(
String::length,
Function.identity(),
(existing, replacement) -> existing
));
}Problem 30: Group Elements
public Map<Integer, List<String>> groupByLength(List<String> strings) {
return strings.stream()
.collect(Collectors.groupingBy(String::length));
}
public Map<Character, List<String>> groupByFirstChar(List<String> strings) {
return strings.stream()
.collect(Collectors.groupingBy(s -> s.charAt(0)));
}
public Map<Boolean, List<Integer>> groupByEvenOdd(List<Integer> numbers) {
return numbers.stream()
.collect(Collectors.groupingBy(n -> n % 2 == 0));
}Problem 31: Partition Elements
public Map<Boolean, List<Integer>> partitionEvenOdd(List<Integer> numbers) {
return numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
}
public Map<Boolean, List<String>> partitionByLength(List<String> strings,
int length) {
return strings.stream()
.collect(Collectors.partitioningBy(s -> s.length() > length));
}Problem 32: Downstream Mapping
public Map<Integer, List<String>> groupAndUpperCase(List<String> strings) {
return strings.stream()
.collect(Collectors.groupingBy(
String::length,
Collectors.mapping(String::toUpperCase,
Collectors.toList())
));
}Problem 33: Downstream Filtering
public Map<Integer, List<String>> groupAndFilterLong(List<String> strings) {
return strings.stream()
.collect(Collectors.groupingBy(
s -> s.charAt(0),
Collectors.filtering(s -> s.length() > 3,
Collectors.toList())
));
}Problem 34: Downstream FlatMapping
public Map<Integer, List<Character>> groupAndFlatMap(List<String> strings) {
return strings.stream()
.collect(Collectors.groupingBy(
String::length,
Collectors.flatMapping(
s -> s.chars().mapToObj(c -> (char) c),
Collectors.toList()
)
));
}Problem 35: Count in Groups
public Map<Integer, Long> countByLength(List<String> strings) {
return strings.stream()
.collect(Collectors.groupingBy(
String::length,
Collectors.counting()
));
}Problem 36: Numeric Aggregations
public Map<Integer, Double> averageLengthByFirstChar(List<String> strings) {
return strings.stream()
.collect(Collectors.groupingBy(
s -> s.charAt(0),
Collectors.averagingInt(String::length)
));
}
public Map<Character, Integer> sumLengthByFirstChar(List<String> strings) {
return strings.stream()
.collect(Collectors.groupingBy(
s -> s.charAt(0),
Collectors.summingInt(String::length)
));
}
public IntSummaryStatistics getStatistics(List<Integer> numbers) {
return numbers.stream()
.collect(Collectors.summarizingInt(Integer::intValue));
}Problem 37: Max/Min in Groups
public Map<Integer, Optional<String>> longestByLength(List<String> strings) {
return strings.stream()
.collect(Collectors.groupingBy(
String::length,
Collectors.maxBy(Comparator.naturalOrder())
));
}Problem 38: Custom Collector
public List<String> customCollector(Stream<String> stream) {
return stream.collect(Collector.of(
ArrayList::new, // supplier
ArrayList::add, // accumulator
(left, right) -> { // combiner
left.addAll(right);
return left;
},
Collector.Characteristics.IDENTITY_FINISH
));
}Problem 39: Basic Aggregations
public int sumOfNumbers(List<Integer> numbers) {
return numbers.stream()
.reduce(0, Integer::sum);
}
public OptionalInt maxOfNumbers(List<Integer> numbers) {
return numbers.stream()
.mapToInt(Integer::intValue)
.max();
}Problem 40: Count Occurrences
public Map<String, Long> frequencyMap(List<String> words) {
return words.stream()
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()
));
}
public Map<Character, Long> charFrequency(String text) {
return text.chars()
.mapToObj(c -> (char) c)
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()
));
}Problem 41: Find Top K
public List<String> topKFrequent(List<String> words, int k) {
return words.stream()
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()
))
.entrySet().stream()
.sorted(Map.Entry.<String, Long>comparingByValue().reversed())
.limit(k)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}Problem 42: Nth Largest Element
public Optional<Integer> nthLargest(List<Integer> numbers, int n) {
return numbers.stream()
.distinct()
.sorted(Comparator.reverseOrder())
.skip(n - 1)
.findFirst();
}
public Optional<Integer> secondHighest(List<Integer> numbers) {
return nthLargest(numbers, 2);
}Problem 43: Complex Aggregation
public Map<String, Integer> totalLengthByFirstChar(List<String> words) {
return words.stream()
.collect(Collectors.groupingBy(
s -> String.valueOf(s.charAt(0)),
Collectors.summingInt(String::length)
));
}Problem 44: Flatten 2D List
public List<Integer> flatten2DList(List<List<Integer>> nestedList) {
return nestedList.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
}Problem 45: Flatten Optionals
public List<String> flattenOptionals(List<Optional<String>> optionals) {
return optionals.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
}Problem 46: Extract Nested Data
class Department {
String name;
List<Employee> employees;
public List<Employee> getEmployees() { return employees; }
}
class Employee {
String name;
public String getName() { return name; }
}
public List<String> getAllEmployeeNames(List<Department> departments) {
return departments.stream()
.flatMap(dept -> dept.getEmployees().stream())
.map(Employee::getName)
.collect(Collectors.toList());
}Problem 47: Expand Relationships
public List<String> expandWords(List<String> sentences) {
return sentences.stream()
.flatMap(sentence -> Arrays.stream(sentence.split("\\s+")))
.collect(Collectors.toList());
}Problem 48: Flatten Stream of Streams
public List<Integer> flattenStreamOfStreams(Stream<Stream<Integer>> streams) {
return streams.flatMap(Function.identity())
.collect(Collectors.toList());
}Problem 49: Convert Optional to Stream
public List<String> filterPresent(List<Optional<String>> optionals) {
return optionals.stream()
.flatMap(Optional::stream)
.collect(Collectors.toList());
}Problem 50: Optional Transformation
public Optional<Integer> mapExample(Optional<String> optional) {
return optional.map(String::length);
}
public Optional<Integer> flatMapExample(Optional<String> optional) {
return optional.flatMap(s ->
s.isEmpty() ? Optional.empty() : Optional.of(s.length())
);
}Problem 51: Default Values
public String orElseExample(Optional<String> optional) {
return optional.orElse("Default");
}
public String orElseGetExample(Optional<String> optional) {
return optional.orElseGet(() -> {
System.out.println("Computing default");
return "Default";
});
}Problem 52: Null-Safe Operations
public Optional<Integer> safeLength(String text) {
return Optional.ofNullable(text)
.map(String::length);
}
public List<Integer> safeLengths(List<String> strings) {
return strings.stream()
.map(s -> Optional.ofNullable(s).map(String::length).orElse(0))
.collect(Collectors.toList());
}Problem 53: Parallel Processing
public long parallelCount(List<Integer> numbers) {
return numbers.parallelStream()
.filter(n -> n % 2 == 0)
.count();
}Problem 54: Sequential vs Parallel
// BAD: Small dataset, parallel overhead > benefit
public int sumSmallList(List<Integer> numbers) {
return numbers.parallelStream().mapToInt(Integer::intValue).sum();
}
// GOOD: Large dataset benefits from parallelism
public long sumLargeList(List<Integer> numbers) {
return numbers.parallelStream().mapToLong(Integer::longValue).sum();
}Problem 55: Avoid Shared Mutable State
// WRONG: Not thread-safe
public List<Integer> wrongParallel(List<Integer> numbers) {
List<Integer> result = new ArrayList<>();
numbers.parallelStream()
.forEach(result::add); // Race condition!
return result;
}
// CORRECT: Use proper collector
public List<Integer> correctParallel(List<Integer> numbers) {
return numbers.parallelStream()
.collect(Collectors.toList());
}Problem 56: Primitive Stream Operations
public int sumPrimitive(List<Integer> numbers) {
return numbers.stream()
.mapToInt(Integer::intValue)
.sum();
}
public OptionalDouble averagePrimitive(int[] numbers) {
return Arrays.stream(numbers).average();
}Problem 57: Convert to Primitive Streams
public int totalLength(List<String> strings) {
return strings.stream()
.mapToInt(String::length)
.sum();
}Problem 58: Get Statistics
public IntSummaryStatistics getStats(List<Integer> numbers) {
return numbers.stream()
.mapToInt(Integer::intValue)
.summaryStatistics();
}
public void printStats(IntSummaryStatistics stats) {
System.out.println("Count: " + stats.getCount());
System.out.println("Sum: " + stats.getSum());
System.out.println("Min: " + stats.getMin());
System.out.println("Max: " + stats.getMax());
System.out.println("Average: " + stats.getAverage());
}Problem 59: Ordered Operations
public List<Integer> orderedProcessing(Set<Integer> numbers) {
return numbers.stream()
.sorted()
.collect(Collectors.toList());
}Problem 60: Order Preservation
public void unorderedPrint(List<Integer> numbers) {
numbers.parallelStream()
.forEach(System.out::println); // May print out of order
}
public void orderedPrint(List<Integer> numbers) {
numbers.parallelStream()
.forEachOrdered(System.out::println); // Preserves order
}Problem 61: Efficient Processing
public boolean hasLongString(List<String> strings) {
return strings.stream()
.anyMatch(s -> s.length() > 10); // Stops at first match
}
public Optional<String> findFirstLong(List<String> strings) {
return strings.stream()
.filter(s -> s.length() > 10)
.findFirst(); // Stops after finding first
}Problem 62: Work with Infinite Streams
public List<Integer> generateFibonacci(int count) {
return Stream.iterate(new int[]{0, 1},
arr -> new int[]{arr[1], arr[0] + arr[1]})
.map(arr -> arr[0])
.limit(count)
.collect(Collectors.toList());
}
public List<Integer> infiniteRandoms(int count) {
return Stream.generate(() -> (int)(Math.random() * 100))
.limit(count)
.collect(Collectors.toList());
}Problem 63: Wrap Checked Exceptions
@FunctionalInterface
interface CheckedFunction<T, R> {
R apply(T t) throws Exception;
}
public static <T, R> Function<T, R> wrap(CheckedFunction<T, R> function) {
return t -> {
try {
return function.apply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
public List<String> readFiles(List<String> filePaths) {
return filePaths.stream()
.map(wrap(path -> Files.readString(Paths.get(path))))
.collect(Collectors.toList());
}Problem 64: Custom Exception Wrapper
public List<Integer> parseIntegers(List<String> strings) {
return strings.stream()
.map(s -> {
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
return 0; // Default value
}
})
.collect(Collectors.toList());
}Problem 65: Continue on Error
public List<Integer> parseSafely(List<String> strings) {
return strings.stream()
.map(s -> {
try {
return Optional.of(Integer.parseInt(s));
} catch (NumberFormatException e) {
return Optional.<Integer>empty();
}
})
.flatMap(Optional::stream)
.collect(Collectors.toList());
}Problem 66: Custom Interface with Exception
@FunctionalInterface
interface ThrowingConsumer<T> {
void accept(T t) throws Exception;
static <T> Consumer<T> unchecked(ThrowingConsumer<T> consumer) {
return t -> {
try {
consumer.accept(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}
}
public void processFiles(List<String> files) {
files.stream()
.forEach(ThrowingConsumer.unchecked(file -> {
Files.delete(Paths.get(file));
}));
}Problem 67: When NOT to Use Streams
// BAD: Stream overhead for simple operation
public int sumBad(List<Integer> numbers) {
return numbers.stream().mapToInt(Integer::intValue).sum();
}
// GOOD: Simple loop is faster
public int sumGood(List<Integer> numbers) {
int sum = 0;
for (int num : numbers) {
sum += num;
}
return sum;
}
// GOOD: Stream makes sense for complex operations
public int sumEvenSquares(List<Integer> numbers) {
return numbers.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.mapToInt(Integer::intValue)
.sum();
}Problem 68: Method References vs Lambdas
// LESS READABLE
public List<String> toLowerBad(List<String> strings) {
return strings.stream()
.map(s -> s.toLowerCase())
.collect(Collectors.toList());
}
// MORE READABLE
public List<String> toLowerGood(List<String> strings) {
return strings.stream()
.map(String::toLowerCase)
.collect(Collectors.toList());
}Problem 69: Proper peek() Usage
// BAD: Using peek() for side effects (wrong purpose)
public List<String> modifyWithPeek(List<String> strings) {
List<String> result = new ArrayList<>();
strings.stream()
.peek(result::add) // WRONG: side effects
.forEach(s -> {}); // Need terminal operation
return result;
}
// GOOD: Use collect() for accumulation
public List<String> modifyCorrectly(List<String> strings) {
return strings.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
}
// ACCEPTABLE: peek() for debugging
public List<String> debugWithPeek(List<String> strings) {
return strings.stream()
.peek(s -> System.out.println("Processing: " + s))
.map(String::toUpperCase)
.collect(Collectors.toList());
}Problem 70: Immutable Approach
// BAD: Mutable accumulation
public List<String> mutableApproach(List<String> strings) {
List<String> result = new ArrayList<>();
strings.stream()
.filter(s -> s.length() > 3)
.forEach(result::add);
return result;
}
// GOOD: Immutable collector
public List<String> immutableApproach(List<String> strings) {
return strings.stream()
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
}Problem 71: Choose Appropriate Approach
// Simple iteration: for-loop is clearer and faster
public void printSimple(List<String> strings) {
for (String s : strings) {
System.out.println(s);
}
}
// Complex pipeline: stream is clearer
public Map<Integer, Long> complexOperation(List<String> strings) {
return strings.stream()
.filter(s -> s.length() > 3)
.map(String::toLowerCase)
.collect(Collectors.groupingBy(
String::length,
Collectors.counting()
));
}Problem 72: Memory-Efficient Processing
// BAD: Loads entire file into memory
public long countLinesInMemory(String filePath) throws IOException {
List<String> lines = Files.readAllLines(Paths.get(filePath));
return lines.size();
}
// GOOD: Streams lines without loading all
public long countLinesStreaming(String filePath) throws IOException {
try (Stream<String> lines = Files.lines(Paths.get(filePath))) {
return lines.count();
}
}Problem 73: Count Word Occurrences
public Map<String, Long> wordFrequency(List<String> words) {
return words.stream()
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()
));
}
public Map<String, Long> wordFrequencyIgnoreCase(List<String> words) {
return words.stream()
.map(String::toLowerCase)
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()
));
}Problem 74: Employee Grouping
class Employee {
String name;
String department;
double salary;
// Constructor, getters
public Employee(String name, String department, double salary) {
this.name = name;
this.department = department;
this.salary = salary;
}
public String getDepartment() { return department; }
public double getSalary() { return salary; }
public String getName() { return name; }
}
public Map<String, List<Employee>> groupByDepartment(List<Employee> employees) {
return employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
}
public Map<String, Long> countByDepartment(List<Employee> employees) {
return employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.counting()
));
}Problem 75: Department-wise Max Salary
public Map<String, Optional<Employee>> highestSalaryByDept(
List<Employee> employees) {
return employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.maxBy(
Comparator.comparingDouble(Employee::getSalary)
)
));
}
public Map<String, Double> maxSalaryByDept(List<Employee> employees) {
return employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.mapping(
Employee::getSalary,
Collectors.maxBy(Double::compareTo)
)
))
.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().orElse(0.0)
));
}Problem 76: Sort Map Entries
public Map<String, Integer> sortMapByValue(Map<String, Integer> map) {
return map.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
}
public Map<String, Integer> sortMapByValueDesc(Map<String, Integer> map) {
return map.entrySet().stream()
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
}Problem 77: Identify Duplicate Elements
public List<String> findDuplicates(List<String> list) {
return list.stream()
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()
))
.entrySet().stream()
.filter(e -> e.getValue() > 1)
.map(Map.Entry::getKey)
.collect(Collectors.toList());
}
public Set<String> findDuplicatesSet(List<String> list) {
Set<String> seen = new HashSet<>();
return list.stream()
.filter(e -> !seen.add(e))
.collect(Collectors.toSet());
}Problem 78: Find First Unique Character
public Optional<Character> firstNonRepeatedChar(String str) {
return str.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();
}
public Optional<String> firstNonRepeatedWord(List<String> words) {
return words.stream()
.collect(Collectors.groupingBy(
Function.identity(),
LinkedHashMap::new,
Collectors.counting()
))
.entrySet().stream()
.filter(e -> e.getValue() == 1)
.map(Map.Entry::getKey)
.findFirst();
}Problem 79: List to Map Conversion
public Map<Integer, String> listToMap(List<String> strings) {
return strings.stream()
.collect(Collectors.toMap(
String::length,
Function.identity(),
(existing, replacement) -> existing // Keep first
));
}
public Map<Integer, String> listToMapKeepLongest(List<String> strings) {
return strings.stream()
.collect(Collectors.toMap(
String::length,
Function.identity(),
(s1, s2) -> s1.length() >= s2.length() ? s1 : s2
));
}
public Map<Integer, List<String>> listToMapMultiValue(List<String> strings) {
return strings.stream()
.collect(Collectors.groupingBy(String::length));
}Problem 80: Combine Multiple Maps
public Map<String, Integer> mergeMaps(Map<String, Integer> map1,
Map<String, Integer> map2) {
return Stream.of(map1, map2)
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
Integer::sum
));
}
public Map<String, Integer> mergeMultipleMaps(List<Map<String, Integer>> maps) {
return maps.stream()
.flatMap(map -> map.entrySet().stream())
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
Integer::sum
));
}Problem 81: Filter Nulls
public List<String> removeNulls(List<String> strings) {
return strings.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
}
public Map<String, Integer> removeNullValues(Map<String, Integer> map) {
return map.entrySet().stream()
.filter(e -> e.getValue() != null)
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue
));
}Problem 82: Split List by Condition
public Map<Boolean, List<Integer>> partitionEvenOdd(List<Integer> numbers) {
return numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
}
public Map<Boolean, List<String>> partitionByLength(List<String> strings) {
return strings.stream()
.collect(Collectors.partitioningBy(s -> s.length() > 5));
}Problem 83: Join Strings
public String joinWithComma(List<String> strings) {
return strings.stream()
.collect(Collectors.joining(", "));
}
public String joinWithPrefix(List<String> strings) {
return strings.stream()
.collect(Collectors.joining(", ", "[", "]"));
}
public String joinFiltered(List<String> strings) {
return strings.stream()
.filter(s -> s.length() > 3)
.collect(Collectors.joining(" | "));
}Problem 84: Statistical Operations
public OptionalDouble average(List<Integer> numbers) {
return numbers.stream()
.mapToInt(Integer::intValue)
.average();
}
public Map<String, Double> averageSalaryByDept(List<Employee> employees) {
return employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.averagingDouble(Employee::getSalary)
));
}Problem 85: Get Top N
public List<Integer> topN(List<Integer> numbers, int n) {
return numbers.stream()
.sorted(Comparator.reverseOrder())
.limit(n)
.collect(Collectors.toList());
}
public List<Employee> topNSalaries(List<Employee> employees, int n) {
return employees.stream()
.sorted(Comparator.comparingDouble(Employee::getSalary)
.reversed())
.limit(n)
.collect(Collectors.toList());
}Problem 86: Conditional Take/Drop
// takeWhile: Take elements while condition is true
public List<Integer> takeWhileLessThan(List<Integer> numbers, int limit) {
return numbers.stream()
.takeWhile(n -> n < limit)
.collect(Collectors.toList());
}
// dropWhile: Drop elements while condition is true
public List<Integer> dropWhileLessThan(List<Integer> numbers, int limit) {
return numbers.stream()
.dropWhile(n -> n < limit)
.collect(Collectors.toList());
}
// Example: Get elements after first negative
public List<Integer> afterFirstNegative(List<Integer> numbers) {
return numbers.stream()
.dropWhile(n -> n >= 0)
.collect(Collectors.toList());
}Problem 87: Iterate with Condition
// Old way (infinite stream + limit)
public List<Integer> generateOld(int start, int count) {
return Stream.iterate(start, n -> n + 1)
.limit(count)
.collect(Collectors.toList());
}
// New way (Java 9+): iterate with predicate
public List<Integer> generateNew(int start, int limit) {
return Stream.iterate(start, n -> n < limit, n -> n + 1)
.collect(Collectors.toList());
}
public List<Integer> fibonacciUntil(int max) {
return Stream.iterate(new int[]{0, 1},
arr -> arr[0] < max,
arr -> new int[]{arr[1], arr[0] + arr[1]})
.map(arr -> arr[0])
.collect(Collectors.toList());
}Problem 88: Create Stream from Nullable
public List<String> streamOfNullable(String value) {
return Stream.ofNullable(value)
.collect(Collectors.toList());
}
public List<String> flatMapNullables(List<String> strings) {
return strings.stream()
.flatMap(Stream::ofNullable)
.collect(Collectors.toList());
}Problem 89: Implement Custom Spliterator
class PairSpliterator<T> implements Spliterator<List<T>> {
private final List<T> list;
private int current = 0;
public PairSpliterator(List<T> list) {
this.list = list;
}
@Override
public boolean tryAdvance(Consumer<? super List<T>> action) {
if (current < list.size() - 1) {
action.accept(Arrays.asList(
list.get(current),
list.get(current + 1)
));
current += 2;
return true;
}
return false;
}
@Override
public Spliterator<List<T>> trySplit() {
return null; // Sequential only
}
@Override
public long estimateSize() {
return (list.size() - current) / 2;
}
@Override
public int characteristics() {
return ORDERED | SIZED | IMMUTABLE;
}
}
public List<List<Integer>> processPairs(List<Integer> numbers) {
return StreamSupport.stream(new PairSpliterator<>(numbers), false)
.collect(Collectors.toList());
}Problem 90: Understand Characteristics
public void demonstrateCharacteristics() {
// ORDERED: Elements have defined encounter order
List<Integer> ordered = Arrays.asList(1, 2, 3);
// DISTINCT: No duplicate elements
Set<Integer> distinct = new HashSet<>(ordered);
// SORTED: Elements are sorted
List<Integer> sorted = ordered.stream()
.sorted()
.collect(Collectors.toList());
// SIZED: Size is known
Stream<Integer> sized = Stream.of(1, 2, 3);
}Problem 91: Understand Lazy Processing
public void demonstrateLazy() {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// This doesn't execute anything
Stream<Integer> stream = numbers.stream()
.filter(n -> {
System.out.println("Filter: " + n);
return n % 2 == 0;
})
.map(n -> {
System.out.println("Map: " + n);
return n * 2;
});
System.out.println("Stream created, nothing executed yet");
// Only now does execution happen
stream.forEach(System.out::println);
}Problem 92: Efficient Short-Circuit
public boolean hasEvenNumberOptimized(List<Integer> numbers) {
// Stops at first even number found
return numbers.stream()
.peek(n -> System.out.println("Checking: " + n))
.anyMatch(n -> n % 2 == 0);
}
public Optional<Integer> findFirstGreaterThan(List<Integer> numbers, int limit) {
// Stops after finding first match
return numbers.stream()
.filter(n -> n > limit)
.findFirst();
}Problem 93: Avoid Stream Reuse
// WRONG: Cannot reuse stream
public void wrongStreamReuse() {
Stream<Integer> stream = Stream.of(1, 2, 3);
long count = stream.count();
// This throws IllegalStateException
// stream.forEach(System.out::println);
}
// CORRECT: Create new stream
public void correctStreamUsage() {
List<Integer> numbers = Arrays.asList(1, 2, 3);
long count = numbers.stream().count();
numbers.stream().forEach(System.out::println); // New stream
}
// CORRECT: Use Supplier for reusable stream source
public void supplierPattern() {
Supplier<Stream<Integer>> streamSupplier =
() -> Stream.of(1, 2, 3);
long count = streamSupplier.get().count();
streamSupplier.get().forEach(System.out::println);
}Problem 94: Combine Two Collectors
public Map<String, Object> calculateStats(List<Integer> numbers) {
return numbers.stream()
.collect(Collectors.teeing(
Collectors.summingInt(Integer::intValue),
Collectors.counting(),
(sum, count) -> Map.of(
"sum", sum,
"count", count,
"average", count > 0 ? (double) sum / count : 0.0
)
));
}
public String minAndMax(List<Integer> numbers) {
return numbers.stream()
.collect(Collectors.teeing(
Collectors.minBy(Integer::compareTo),
Collectors.maxBy(Integer::compareTo),
(min, max) -> String.format("Min: %d, Max: %d",
min.orElse(0), max.orElse(0))
));
}Problem 95: Filter in Downstream Collector
public Map<String, List<Employee>> filterHighSalaryByDept(
List<Employee> employees, double minSalary) {
return employees.stream()
.collect(Collectors.groupingBy(
Employee::getDepartment,
Collectors.filtering(
e -> e.getSalary() > minSalary,
Collectors.toList()
)
));
}Problem 96: FlatMap in Downstream
class Department {
String name;
List<Employee> employees;
public String getName() { return name; }
public List<Employee> getEmployees() { return employees; }
}
public Map<String, List<String>> getEmployeeNamesByDept(
List<Department> departments) {
return departments.stream()
.collect(Collectors.toMap(
Department::getName,
dept -> dept.getEmployees().stream()
.map(Employee::getName)
.collect(Collectors.toList())
));
}
public Map<String, Set<String>> flatMapCollector(List<Department> departments) {
return departments.stream()
.collect(Collectors.groupingBy(
Department::getName,
Collectors.flatMapping(
dept -> dept.getEmployees().stream()
.map(Employee::getName),
Collectors.toSet()
)
));
}This comprehensive guide covers all essential Java Streams concepts with 96 practical problems:
- Foundation: Stream basics, creation, and pipeline structure
- Operations: All intermediate and terminal operations with examples
- Collectors: Complete coverage of built-in and custom collectors
- Patterns: Common patterns like flatMap, grouping, and partitioning
- Advanced: Error handling, performance, parallel streams, and Java 9+ features
- Interview Problems: Real-world scenarios frequently asked in interviews
- Use streams for complex data transformations, not simple iterations
- Prefer method references over lambdas for readability
- Avoid stateful lambdas in parallel streams
- Use primitive streams (IntStream, LongStream) to avoid boxing overhead
- Handle exceptions properly with custom wrappers
- Leverage collectors for powerful data aggregations
- Understand lazy evaluation to write efficient pipelines
- Be cautious with parallel streams - they're not always faster
- Start with basic operations (map, filter, collect)
- Master collectors and groupingBy patterns
- Practice flatMap scenarios extensively
- Solve interview problems multiple times
- Benchmark parallel vs sequential for your use cases
- Read source code of Stream API for deeper understanding
Happy Streaming! 🚀