Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save carefree-ladka/304728591ef51a0e2f0f039c2794c6ba to your computer and use it in GitHub Desktop.

Select an option

Save carefree-ladka/304728591ef51a0e2f0f039c2794c6ba to your computer and use it in GitHub Desktop.
Complete Java Streams Guide with Problems

Complete Java Streams Guide with Problems

Table of Contents

  1. Stream Basics (Foundation)
  2. Creating Streams
  3. Intermediate Operations
  4. Terminal Operations
  5. Collectors (Very Important 🚀)
  6. Reduction & Aggregation Patterns
  7. FlatMap Patterns (Interview Favorite)
  8. Optional + Streams
  9. Parallel Streams
  10. Primitive Streams
  11. Stream Ordering & Characteristics
  12. Error Handling in Streams
  13. Performance & Best Practices
  14. Common Interview Stream Problems
  15. Advanced / Lesser-Known Topics

1. Stream Basics (Foundation)

What is a Stream?

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

Stream vs Collection

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());
}

Lazy Evaluation

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());
}

Stream Pipeline

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
}

One-Time Consumption Rule

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);
}

Sequential vs Parallel Streams

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();
}

2. Creating Streams

Collection.stream() / parallelStream()

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());
}

Arrays.stream()

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);
}

Stream.of()

Problem 8: Create Stream Directly

public List<String> createStreamOf() {
    return Stream.of("Java", "Python", "JavaScript", "C++")
                 .filter(lang -> lang.startsWith("J"))
                 .collect(Collectors.toList());
}

Stream.generate()

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());
}

Stream.iterate()

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());
}

IntStream, LongStream, DoubleStream

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());
}

Files.lines() / Files.walk()

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());
    }
}

Streams from Optional

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());
}

3. Intermediate Operations

map

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());
}

flatMap

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());
}

filter

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());
}

peek

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());
}

distinct

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());
}

sorted

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());
}

limit

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());
}

skip

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());
}

4. Terminal Operations

reduce

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);
}

collect

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(", "));
}

count

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();
}

min, max

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));
}

forEach, forEachOrdered

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);
}

anyMatch, allMatch, noneMatch

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);
}

findFirst, findAny

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();
}

5. Collectors (Very Important 🚀)

toList(), toSet(), toMap()

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
                  ));
}

groupingBy

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));
}

partitioningBy

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));
}

mapping

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())
                  ));
}

filtering

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())
                  ));
}

flatMapping

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()
                      )
                  ));
}

counting

Problem 35: Count in Groups

public Map<Integer, Long> countByLength(List<String> strings) {
    return strings.stream()
                  .collect(Collectors.groupingBy(
                      String::length,
                      Collectors.counting()
                  ));
}

averagingInt, summingInt, summarizingInt

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));
}

maxBy, minBy

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())
                  ));
}

Collector.of() - Custom Collectors

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
    ));
}

6. Reduction & Aggregation Patterns

Sum / Product / Max / Min

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();
}

Frequency Map

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()
               ));
}

Top-K Elements

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());
}

Second Highest / Nth Largest

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);
}

Group → Reduce → Transform

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)
                ));
}

7. FlatMap Patterns (Interview Favorite)

List<List> → List

Problem 44: Flatten 2D List

public List<Integer> flatten2DList(List<List<Integer>> nestedList) {
    return nestedList.stream()
                     .flatMap(List::stream)
                     .collect(Collectors.toList());
}

Flattening Optional

Problem 45: Flatten Optionals

public List<String> flattenOptionals(List<Optional<String>> optionals) {
    return optionals.stream()
                    .flatMap(Optional::stream)
                    .collect(Collectors.toList());
}

Mapping Nested Objects

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());
}

One-to-Many Relationships

Problem 47: Expand Relationships

public List<String> expandWords(List<String> sentences) {
    return sentences.stream()
                    .flatMap(sentence -> Arrays.stream(sentence.split("\\s+")))
                    .collect(Collectors.toList());
}

Streams of Streams

Problem 48: Flatten Stream of Streams

public List<Integer> flattenStreamOfStreams(Stream<Stream<Integer>> streams) {
    return streams.flatMap(Function.identity())
                  .collect(Collectors.toList());
}

8. Optional + Streams

Optional.stream()

Problem 49: Convert Optional to Stream

public List<String> filterPresent(List<Optional<String>> optionals) {
    return optionals.stream()
                    .flatMap(Optional::stream)
                    .collect(Collectors.toList());
}

map vs flatMap

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())
    );
}

orElse vs orElseGet

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";
    });
}

Avoiding NullPointerException

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());
}

9. Parallel Streams

How Parallel Streams Work

Problem 53: Parallel Processing

public long parallelCount(List<Integer> numbers) {
    return numbers.parallelStream()
                  .filter(n -> n % 2 == 0)
                  .count();
}

When NOT to Use Parallel Streams

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();
}

Thread-Safety Issues

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());
}

10. Primitive Streams

IntStream, LongStream, DoubleStream

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();
}

mapToInt, mapToLong

Problem 57: Convert to Primitive Streams

public int totalLength(List<String> strings) {
    return strings.stream()
                  .mapToInt(String::length)
                  .sum();
}

summaryStatistics

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());
}

11. Stream Ordering & Characteristics

Ordered vs Unordered

Problem 59: Ordered Operations

public List<Integer> orderedProcessing(Set<Integer> numbers) {
    return numbers.stream()
                  .sorted()
                  .collect(Collectors.toList());
}

forEach vs forEachOrdered

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
}

Short-Circuiting Operations

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
}

Infinite Streams

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());
}

12. Error Handling in Streams

Handling Checked Exceptions

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());
}

Wrapping Exceptions in Lambdas

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());
}

Fail-Fast vs Fail-Safe

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());
}

Custom Functional Interfaces

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));
         }));
}

13. Performance & Best Practices

Avoid Unnecessary Streams

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();
}

Prefer Method References

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());
}

Avoid peek() Misuse

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());
}

Collectors vs Mutation

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());
}

Stream vs For-Loop Tradeoffs

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()
                  ));
}

Memory Considerations

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();
    }
}

14. Common Interview Stream Problems

Frequency Map from List

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()
                ));
}

Group Employees by Department

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()
                    ));
}

Highest Salary per Department

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)
                    ));
}

Sort Map by Value

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
              ));
}

Find Duplicates

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());
}

First Non-Repeated Element

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();
}

Convert List to Map with Conflict Handling

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));
}

Merge Maps Using Streams

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
               ));
}

Remove Null Values

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
              ));
}

Partition List

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));
}

String Joining

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(" | "));
}

Average, Min, Max

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)
                    ));
}

Top N Elements

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());
}

15. Advanced / Lesser-Known Topics

takeWhile & dropWhile (Java 9+)

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());
}

iterate() with Predicate (Java 9+)

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());
}

ofNullable (Java 9+)

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());
}

Custom Spliterator

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());
}

Stream Characteristics

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);
}

Lazy Evaluation Internals

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);
}

Short-Circuiting Optimization

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();
}

Stream Reuse Pitfalls

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);
}

Teeing Collector (Java 12+)

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))
                  ));
}

Filtering Collector (Java 9+)

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()
                        )
                    ));
}

FlatMapping Collector (Java 9+)

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()
                          )
                      ));
}

Summary

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

Key Takeaways

  1. Use streams for complex data transformations, not simple iterations
  2. Prefer method references over lambdas for readability
  3. Avoid stateful lambdas in parallel streams
  4. Use primitive streams (IntStream, LongStream) to avoid boxing overhead
  5. Handle exceptions properly with custom wrappers
  6. Leverage collectors for powerful data aggregations
  7. Understand lazy evaluation to write efficient pipelines
  8. Be cautious with parallel streams - they're not always faster

Practice Tips

  • 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! 🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment