Skip to content

Instantly share code, notes, and snippets.

@carefree-ladka
Created February 13, 2026 07:03
Show Gist options
  • Select an option

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

Select an option

Save carefree-ladka/1706452acbc865358d66e6195f7e2810 to your computer and use it in GitHub Desktop.
Java Streams Complete Guide

Java Streams Complete Guide 🚀

Master Java Streams API with 100+ Problems & Solutions


Table of Contents

  1. Stream Basics (Warm-up)
  2. Mapping & Transformation
  3. Filtering & Matching
  4. Reduction & Aggregation
  5. Grouping & Partitioning
  6. Collectors Deep Practice
  7. Advanced Interview Level Problems
  8. Streams with Objects
  9. Parallel Streams
  10. Tricky Conceptual Questions
  11. 15 MUST-DO Interview Problems

1. Stream Basics (Warm-up)

Concepts Covered

  • stream() - Create a stream from collection
  • filter() - Filter elements based on predicate
  • map() - Transform elements
  • distinct() - Remove duplicates
  • count() - Count elements
  • sorted() - Sort elements
  • collect() - Collect results

Problem 1.1: Filter Even Numbers

import java.util.*;
import java.util.stream.*;

public class StreamBasics {
    
    // Filter even numbers from a list
    public static List<Integer> filterEvenNumbers(List<Integer> numbers) {
        return numbers.stream()
                      .filter(n -> n % 2 == 0)
                      .collect(Collectors.toList());
    }
    
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        System.out.println("Even numbers: " + filterEvenNumbers(numbers));
        // Output: [2, 4, 6, 8, 10]
    }
}

Problem 1.2: Convert Strings to Uppercase

public static List<String> convertToUppercase(List<String> strings) {
    return strings.stream()
                  .map(String::toUpperCase)
                  .collect(Collectors.toList());
}

// Example usage
List<String> words = Arrays.asList("java", "python", "javascript");
System.out.println(convertToUppercase(words));
// Output: [JAVA, PYTHON, JAVASCRIPT]

Problem 1.3: Find Squares of Numbers

public static List<Integer> findSquares(List<Integer> numbers) {
    return numbers.stream()
                  .map(n -> n * n)
                  .collect(Collectors.toList());
}

// Example usage
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
System.out.println(findSquares(nums));
// Output: [1, 4, 9, 16, 25]

Problem 1.4: Remove Duplicates

public static List<Integer> removeDuplicates(List<Integer> numbers) {
    return numbers.stream()
                  .distinct()
                  .collect(Collectors.toList());
}

// Example usage
List<Integer> nums = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
System.out.println(removeDuplicates(nums));
// Output: [1, 2, 3, 4, 5]

Problem 1.5: Count Elements Greater Than Given Number

public static long countGreaterThan(List<Integer> numbers, int threshold) {
    return numbers.stream()
                  .filter(n -> n > threshold)
                  .count();
}

// Example usage
List<Integer> nums = Arrays.asList(1, 5, 10, 15, 20, 25);
System.out.println("Count > 10: " + countGreaterThan(nums, 10));
// Output: 3

Problem 1.6: Sort in Ascending/Descending Order

// Ascending order
public static List<Integer> sortAscending(List<Integer> numbers) {
    return numbers.stream()
                  .sorted()
                  .collect(Collectors.toList());
}

// Descending order
public static List<Integer> sortDescending(List<Integer> numbers) {
    return numbers.stream()
                  .sorted(Comparator.reverseOrder())
                  .collect(Collectors.toList());
}

// Example usage
List<Integer> nums = Arrays.asList(5, 2, 8, 1, 9);
System.out.println("Ascending: " + sortAscending(nums));
System.out.println("Descending: " + sortDescending(nums));
// Output: 
// Ascending: [1, 2, 5, 8, 9]
// Descending: [9, 8, 5, 2, 1]

2. Mapping & Transformation

Concepts Covered

  • map() - One-to-one transformation
  • flatMap() - One-to-many transformation (flattening)
  • Collectors.joining() - Join strings
  • Collectors.toSet() - Collect to Set

Problem 2.1: Convert List → List (String Lengths)

public static List<Integer> getStringLengths(List<String> strings) {
    return strings.stream()
                  .map(String::length)
                  .collect(Collectors.toList());
}

// Example usage
List<String> words = Arrays.asList("Java", "Streams", "API");
System.out.println(getStringLengths(words));
// Output: [4, 7, 3]

Problem 2.2: Extract Specific Field from List of Objects

class Person {
    String name;
    int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() { return name; }
    public int getAge() { return age; }
}

public static List<String> extractNames(List<Person> persons) {
    return persons.stream()
                  .map(Person::getName)
                  .collect(Collectors.toList());
}

// Example usage
List<Person> people = Arrays.asList(
    new Person("Alice", 25),
    new Person("Bob", 30),
    new Person("Charlie", 35)
);
System.out.println(extractNames(people));
// Output: [Alice, Bob, Charlie]

Problem 2.3: Flatten a List<List>

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

// Example usage
List<List<Integer>> nested = Arrays.asList(
    Arrays.asList(1, 2, 3),
    Arrays.asList(4, 5),
    Arrays.asList(6, 7, 8, 9)
);
System.out.println(flattenList(nested));
// Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]

Problem 2.4: Convert List to Comma-Separated String

public static String toCommaSeparatedString(List<String> strings) {
    return strings.stream()
                  .collect(Collectors.joining(", "));
}

// Example usage
List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");
System.out.println(toCommaSeparatedString(fruits));
// Output: Apple, Banana, Orange

Problem 2.5: Convert List to Set

public static Set<Integer> listToSet(List<Integer> numbers) {
    return numbers.stream()
                  .collect(Collectors.toSet());
}

// Example usage
List<Integer> nums = Arrays.asList(1, 2, 2, 3, 4, 4, 5);
System.out.println(listToSet(nums));
// Output: [1, 2, 3, 4, 5] (order may vary)

3. Filtering & Matching

Concepts Covered

  • allMatch() - Check if all elements match predicate
  • anyMatch() - Check if any element matches
  • noneMatch() - Check if no element matches
  • findFirst() - Get first element
  • findAny() - Get any element
  • Optional - Handle potential absence of values

Problem 3.1: Check if All Numbers Are Positive

public static boolean areAllPositive(List<Integer> numbers) {
    return numbers.stream()
                  .allMatch(n -> n > 0);
}

// Example usage
List<Integer> nums1 = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> nums2 = Arrays.asList(1, -2, 3, 4, 5);
System.out.println("All positive (nums1): " + areAllPositive(nums1)); // true
System.out.println("All positive (nums2): " + areAllPositive(nums2)); // false

Problem 3.2: Check if Any String Starts with "A"

public static boolean anyStartsWithA(List<String> strings) {
    return strings.stream()
                  .anyMatch(s -> s.startsWith("A"));
}

// Example usage
List<String> words = Arrays.asList("Apple", "Banana", "Cherry");
System.out.println("Any starts with A: " + anyStartsWithA(words));
// Output: true

Problem 3.3: Find First Element Greater Than 50

public static Optional<Integer> findFirstGreaterThan50(List<Integer> numbers) {
    return numbers.stream()
                  .filter(n -> n > 50)
                  .findFirst();
}

// Example usage
List<Integer> nums = Arrays.asList(10, 20, 60, 30, 70);
Optional<Integer> result = findFirstGreaterThan50(nums);
result.ifPresent(n -> System.out.println("First > 50: " + n));
// Output: First > 50: 60

Problem 3.4: Get Any Element Divisible by 3

public static Optional<Integer> findAnyDivisibleBy3(List<Integer> numbers) {
    return numbers.stream()
                  .filter(n -> n % 3 == 0)
                  .findAny();
}

// Example usage
List<Integer> nums = Arrays.asList(10, 15, 20, 30);
Optional<Integer> result = findAnyDivisibleBy3(nums);
result.ifPresent(n -> System.out.println("Divisible by 3: " + n));
// Output: Divisible by 3: 15 (or 30)

Problem 3.5: Filter Employees with Salary > 50k

class Employee {
    String name;
    double salary;
    
    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
    
    public String getName() { return name; }
    public double getSalary() { return salary; }
}

public static List<Employee> filterHighEarners(List<Employee> employees) {
    return employees.stream()
                    .filter(e -> e.getSalary() > 50000)
                    .collect(Collectors.toList());
}

// Example usage
List<Employee> employees = Arrays.asList(
    new Employee("Alice", 60000),
    new Employee("Bob", 45000),
    new Employee("Charlie", 75000)
);
List<Employee> highEarners = filterHighEarners(employees);
highEarners.forEach(e -> System.out.println(e.getName() + ": $" + e.getSalary()));
// Output:
// Alice: $60000.0
// Charlie: $75000.0

4. Reduction & Aggregation

Concepts Covered

  • reduce() - Reduce stream to single value
  • max() / min() - Find maximum/minimum
  • Collectors.summarizingInt() - Get statistics
  • Collectors.groupingBy() - Group elements
  • Collectors.counting() - Count elements in groups

Problem 4.1: Find Sum of Numbers

// Using reduce
public static int findSum(List<Integer> numbers) {
    return numbers.stream()
                  .reduce(0, Integer::sum);
}

// Using sum() with mapToInt
public static int findSumAlternative(List<Integer> numbers) {
    return numbers.stream()
                  .mapToInt(Integer::intValue)
                  .sum();
}

// Example usage
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
System.out.println("Sum: " + findSum(nums));
// Output: Sum: 15

Problem 4.2: Find Max/Min in List

public static Optional<Integer> findMax(List<Integer> numbers) {
    return numbers.stream()
                  .max(Integer::compareTo);
}

public static Optional<Integer> findMin(List<Integer> numbers) {
    return numbers.stream()
                  .min(Integer::compareTo);
}

// Example usage
List<Integer> nums = Arrays.asList(5, 2, 8, 1, 9);
findMax(nums).ifPresent(max -> System.out.println("Max: " + max));
findMin(nums).ifPresent(min -> System.out.println("Min: " + min));
// Output:
// Max: 9
// Min: 1

Problem 4.3: Find Second Highest Number

public static Optional<Integer> findSecondHighest(List<Integer> numbers) {
    return numbers.stream()
                  .distinct()
                  .sorted(Comparator.reverseOrder())
                  .skip(1)
                  .findFirst();
}

// Example usage
List<Integer> nums = Arrays.asList(5, 2, 8, 1, 9, 8);
findSecondHighest(nums).ifPresent(n -> System.out.println("Second highest: " + n));
// Output: Second highest: 8

Problem 4.4: Calculate Average

public static OptionalDouble calculateAverage(List<Integer> numbers) {
    return numbers.stream()
                  .mapToInt(Integer::intValue)
                  .average();
}

// Example usage
List<Integer> nums = Arrays.asList(10, 20, 30, 40, 50);
calculateAverage(nums).ifPresent(avg -> System.out.println("Average: " + avg));
// Output: Average: 30.0

Problem 4.5: Count Frequency of Each Element

public static Map<Integer, Long> countFrequency(List<Integer> numbers) {
    return numbers.stream()
                  .collect(Collectors.groupingBy(
                      n -> n,
                      Collectors.counting()
                  ));
}

// Example usage
List<Integer> nums = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 4, 4, 4);
System.out.println(countFrequency(nums));
// Output: {1=1, 2=2, 3=3, 4=4}

Problem 4.6: Get Summary Statistics

public static IntSummaryStatistics getStatistics(List<Integer> numbers) {
    return numbers.stream()
                  .collect(Collectors.summarizingInt(Integer::intValue));
}

// Example usage
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5);
IntSummaryStatistics stats = getStatistics(nums);
System.out.println("Count: " + stats.getCount());
System.out.println("Sum: " + stats.getSum());
System.out.println("Average: " + stats.getAverage());
System.out.println("Min: " + stats.getMin());
System.out.println("Max: " + stats.getMax());

5. Grouping & Partitioning

Concepts Covered

  • Collectors.groupingBy() - Group elements by classifier
  • Collectors.partitioningBy() - Partition into two groups
  • Downstream collectors - Additional operations on grouped data

Problem 5.1: Group Employees by Department

class Employee {
    String name;
    String department;
    double salary;
    
    public Employee(String name, String department, double salary) {
        this.name = name;
        this.department = department;
        this.salary = salary;
    }
    
    public String getName() { return name; }
    public String getDepartment() { return department; }
    public double getSalary() { return salary; }
}

public static Map<String, List<Employee>> groupByDepartment(List<Employee> employees) {
    return employees.stream()
                    .collect(Collectors.groupingBy(Employee::getDepartment));
}

// Example usage
List<Employee> employees = Arrays.asList(
    new Employee("Alice", "IT", 60000),
    new Employee("Bob", "HR", 50000),
    new Employee("Charlie", "IT", 70000),
    new Employee("David", "HR", 55000)
);
Map<String, List<Employee>> grouped = groupByDepartment(employees);
grouped.forEach((dept, empList) -> {
    System.out.println(dept + ": " + empList.size() + " employees");
});
// Output:
// IT: 2 employees
// HR: 2 employees

Problem 5.2: Group Numbers by Even/Odd

public static Map<Boolean, List<Integer>> groupByEvenOdd(List<Integer> numbers) {
    return numbers.stream()
                  .collect(Collectors.partitioningBy(n -> n % 2 == 0));
}

// Example usage
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Map<Boolean, List<Integer>> grouped = groupByEvenOdd(nums);
System.out.println("Even: " + grouped.get(true));
System.out.println("Odd: " + grouped.get(false));
// Output:
// Even: [2, 4, 6, 8, 10]
// Odd: [1, 3, 5, 7, 9]

Problem 5.3: Partition Numbers into Prime & Non-Prime

public static boolean isPrime(int n) {
    if (n <= 1) return false;
    for (int i = 2; i <= Math.sqrt(n); i++) {
        if (n % i == 0) return false;
    }
    return true;
}

public static Map<Boolean, List<Integer>> partitionPrimes(List<Integer> numbers) {
    return numbers.stream()
                  .collect(Collectors.partitioningBy(StreamProblems::isPrime));
}

// Example usage
List<Integer> nums = Arrays.asList(2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
Map<Boolean, List<Integer>> partitioned = partitionPrimes(nums);
System.out.println("Primes: " + partitioned.get(true));
System.out.println("Non-primes: " + partitioned.get(false));
// Output:
// Primes: [2, 3, 5, 7, 11]
// Non-primes: [4, 6, 8, 9, 10]

Problem 5.4: Count Employees Per Department

public static Map<String, Long> countEmployeesPerDepartment(List<Employee> employees) {
    return employees.stream()
                    .collect(Collectors.groupingBy(
                        Employee::getDepartment,
                        Collectors.counting()
                    ));
}

// Example usage
List<Employee> employees = Arrays.asList(
    new Employee("Alice", "IT", 60000),
    new Employee("Bob", "HR", 50000),
    new Employee("Charlie", "IT", 70000),
    new Employee("David", "Finance", 65000)
);
System.out.println(countEmployeesPerDepartment(employees));
// Output: {IT=2, HR=1, Finance=1}

Problem 5.5: Find Highest Salary Per Department

public static Map<String, Optional<Employee>> highestSalaryPerDept(List<Employee> employees) {
    return employees.stream()
                    .collect(Collectors.groupingBy(
                        Employee::getDepartment,
                        Collectors.maxBy(Comparator.comparingDouble(Employee::getSalary))
                    ));
}

// Alternative: Get just the salary value
public static Map<String, Double> maxSalaryPerDept(List<Employee> employees) {
    return employees.stream()
                    .collect(Collectors.groupingBy(
                        Employee::getDepartment,
                        Collectors.collectingAndThen(
                            Collectors.maxBy(Comparator.comparingDouble(Employee::getSalary)),
                            emp -> emp.map(Employee::getSalary).orElse(0.0)
                        )
                    ));
}

// Example usage
List<Employee> employees = Arrays.asList(
    new Employee("Alice", "IT", 60000),
    new Employee("Bob", "HR", 50000),
    new Employee("Charlie", "IT", 70000),
    new Employee("David", "HR", 55000)
);
System.out.println(maxSalaryPerDept(employees));
// Output: {IT=70000.0, HR=55000.0}

Problem 5.6: Multi-level Grouping (Department → Role)

class Employee {
    String name;
    String department;
    String role;
    double salary;
    
    public Employee(String name, String department, String role, double salary) {
        this.name = name;
        this.department = department;
        this.role = role;
        this.salary = salary;
    }
    
    public String getName() { return name; }
    public String getDepartment() { return department; }
    public String getRole() { return role; }
    public double getSalary() { return salary; }
}

public static Map<String, Map<String, List<Employee>>> multiLevelGrouping(List<Employee> employees) {
    return employees.stream()
                    .collect(Collectors.groupingBy(
                        Employee::getDepartment,
                        Collectors.groupingBy(Employee::getRole)
                    ));
}

// Example usage
List<Employee> employees = Arrays.asList(
    new Employee("Alice", "IT", "Developer", 60000),
    new Employee("Bob", "IT", "Manager", 80000),
    new Employee("Charlie", "HR", "Recruiter", 50000),
    new Employee("David", "IT", "Developer", 65000)
);
Map<String, Map<String, List<Employee>>> grouped = multiLevelGrouping(employees);
System.out.println("IT Developers: " + grouped.get("IT").get("Developer").size());
// Output: IT Developers: 2

6. Collectors Deep Practice

Concepts Covered

  • Collectors.toMap() - Collect to Map
  • Merge functions for duplicate keys
  • Collectors.toUnmodifiableList() - Create immutable collections
  • Custom collectors

Problem 6.1: Convert List to Map (ID → Object)

class Product {
    int id;
    String name;
    double price;
    
    public Product(int id, String name, double price) {
        this.id = id;
        this.name = name;
        this.price = price;
    }
    
    public int getId() { return id; }
    public String getName() { return name; }
    public double getPrice() { return price; }
}

public static Map<Integer, Product> listToMap(List<Product> products) {
    return products.stream()
                   .collect(Collectors.toMap(
                       Product::getId,
                       product -> product
                   ));
}

// Example usage
List<Product> products = Arrays.asList(
    new Product(1, "Laptop", 999.99),
    new Product(2, "Mouse", 29.99),
    new Product(3, "Keyboard", 79.99)
);
Map<Integer, Product> productMap = listToMap(products);
System.out.println("Product 2: " + productMap.get(2).getName());
// Output: Product 2: Mouse

Problem 6.2: Handle Duplicate Keys in toMap

public static Map<String, Integer> listToMapWithDuplicates(List<String> words) {
    return words.stream()
                .collect(Collectors.toMap(
                    word -> word,
                    String::length,
                    (existing, replacement) -> existing  // Keep existing value
                ));
}

// Example usage
List<String> words = Arrays.asList("apple", "banana", "apple", "cherry");
System.out.println(listToMapWithDuplicates(words));
// Output: {banana=6, apple=5, cherry=6}

Problem 6.3: Create Unmodifiable List

public static List<String> createUnmodifiableList(List<String> source) {
    return source.stream()
                 .collect(Collectors.toUnmodifiableList());
}

// Example usage
List<String> words = Arrays.asList("Java", "Python", "JavaScript");
List<String> immutable = createUnmodifiableList(words);
// immutable.add("C++"); // This would throw UnsupportedOperationException

Problem 6.4: Create LinkedHashMap Using Streams

public static Map<String, Integer> createLinkedHashMap(List<String> words) {
    return words.stream()
                .collect(Collectors.toMap(
                    word -> word,
                    String::length,
                    (v1, v2) -> v1,
                    LinkedHashMap::new  // Maintain insertion order
                ));
}

// Example usage
List<String> words = Arrays.asList("zebra", "apple", "mango");
System.out.println(createLinkedHashMap(words));
// Output: {zebra=5, apple=5, mango=5} (maintains order)

Problem 6.5: collect() vs reduce() Example

// Using reduce - returns Optional
public static Optional<String> concatenateWithReduce(List<String> strings) {
    return strings.stream()
                  .reduce((s1, s2) -> s1 + s2);
}

// Using collect - returns concrete result
public static String concatenateWithCollect(List<String> strings) {
    return strings.stream()
                  .collect(Collectors.joining());
}

// Example usage
List<String> words = Arrays.asList("Hello", " ", "World");
System.out.println("Reduce: " + concatenateWithReduce(words).orElse(""));
System.out.println("Collect: " + concatenateWithCollect(words));
// Both output: Hello World

7. Advanced Interview Level Problems

Problem 7.1: Find Longest String in List

public static Optional<String> findLongestString(List<String> strings) {
    return strings.stream()
                  .max(Comparator.comparingInt(String::length));
}

// Example usage
List<String> words = Arrays.asList("Java", "JavaScript", "Python", "C");
findLongestString(words).ifPresent(s -> System.out.println("Longest: " + s));
// Output: Longest: JavaScript

Problem 7.2: Find First Non-Repeated Character in String

public static Optional<Character> firstNonRepeatedChar(String str) {
    return str.chars()
              .mapToObj(c -> (char) c)
              .collect(Collectors.groupingBy(c -> c, LinkedHashMap::new, Collectors.counting()))
              .entrySet()
              .stream()
              .filter(entry -> entry.getValue() == 1)
              .map(Map.Entry::getKey)
              .findFirst();
}

// Example usage
String text = "programming";
firstNonRepeatedChar(text).ifPresent(c -> System.out.println("First non-repeated: " + c));
// Output: First non-repeated: p

Problem 7.3: Find Duplicate Elements

public static List<Integer> findDuplicates(List<Integer> numbers) {
    return numbers.stream()
                  .collect(Collectors.groupingBy(n -> n, Collectors.counting()))
                  .entrySet()
                  .stream()
                  .filter(entry -> entry.getValue() > 1)
                  .map(Map.Entry::getKey)
                  .collect(Collectors.toList());
}

// Alternative using Set
public static Set<Integer> findDuplicatesWithSet(List<Integer> numbers) {
    Set<Integer> seen = new HashSet<>();
    return numbers.stream()
                  .filter(n -> !seen.add(n))
                  .collect(Collectors.toSet());
}

// Example usage
List<Integer> nums = Arrays.asList(1, 2, 3, 2, 4, 5, 3, 6);
System.out.println("Duplicates: " + findDuplicates(nums));
// Output: Duplicates: [2, 3]

Problem 7.4: Find Kth Largest Number

public static Optional<Integer> findKthLargest(List<Integer> numbers, int k) {
    return numbers.stream()
                  .distinct()
                  .sorted(Comparator.reverseOrder())
                  .skip(k - 1)
                  .findFirst();
}

// Example usage
List<Integer> nums = Arrays.asList(5, 2, 8, 1, 9, 3);
findKthLargest(nums, 2).ifPresent(n -> System.out.println("2nd largest: " + n));
// Output: 2nd largest: 8

Problem 7.5: Merge Two Lists and Remove Duplicates

public static List<Integer> mergeLists(List<Integer> list1, List<Integer> list2) {
    return Stream.concat(list1.stream(), list2.stream())
                 .distinct()
                 .sorted()
                 .collect(Collectors.toList());
}

// Example usage
List<Integer> list1 = Arrays.asList(1, 2, 3, 4);
List<Integer> list2 = Arrays.asList(3, 4, 5, 6);
System.out.println(mergeLists(list1, list2));
// Output: [1, 2, 3, 4, 5, 6]

Problem 7.6: Word Frequency in Paragraph

public static Map<String, Long> wordFrequency(String paragraph) {
    return Arrays.stream(paragraph.toLowerCase().split("\\s+"))
                 .collect(Collectors.groupingBy(
                     word -> word,
                     Collectors.counting()
                 ));
}

// Example usage
String text = "Java streams are powerful Java streams are elegant";
System.out.println(wordFrequency(text));
// Output: {java=2, streams=2, are=2, powerful=1, elegant=1}

Problem 7.7: Check if Two Strings are Anagrams

public static boolean areAnagrams(String str1, String str2) {
    String sorted1 = str1.chars()
                         .sorted()
                         .collect(StringBuilder::new, 
                                  StringBuilder::appendCodePoint, 
                                  StringBuilder::append)
                         .toString();
    
    String sorted2 = str2.chars()
                         .sorted()
                         .collect(StringBuilder::new, 
                                  StringBuilder::appendCodePoint, 
                                  StringBuilder::append)
                         .toString();
    
    return sorted1.equals(sorted2);
}

// Example usage
System.out.println(areAnagrams("listen", "silent")); // true
System.out.println(areAnagrams("hello", "world"));   // false

Problem 7.8: Find Top 3 Highest Paid Employees

public static List<Employee> findTop3HighestPaid(List<Employee> employees) {
    return employees.stream()
                    .sorted(Comparator.comparingDouble(Employee::getSalary).reversed())
                    .limit(3)
                    .collect(Collectors.toList());
}

// Example usage
List<Employee> employees = Arrays.asList(
    new Employee("Alice", "IT", 60000),
    new Employee("Bob", "HR", 50000),
    new Employee("Charlie", "IT", 70000),
    new Employee("David", "Finance", 80000),
    new Employee("Eve", "IT", 65000)
);
findTop3HighestPaid(employees).forEach(e -> 
    System.out.println(e.getName() + ": $" + e.getSalary())
);
// Output:
// David: $80000.0
// Charlie: $70000.0
// Eve: $65000.0

8. Streams with Objects

Employee Class Definition

class Employee {
    private int id;
    private String name;
    private String department;
    private double salary;
    private int age;
    
    public Employee(int id, String name, String department, double salary, int age) {
        this.id = id;
        this.name = name;
        this.department = department;
        this.salary = salary;
        this.age = age;
    }
    
    // Getters and Setters
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    
    public String getDepartment() { return department; }
    public void setDepartment(String department) { this.department = department; }
    
    public double getSalary() { return salary; }
    public void setSalary(double salary) { this.salary = salary; }
    
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    
    @Override
    public String toString() {
        return "Employee{id=" + id + ", name='" + name + "', dept='" + 
               department + "', salary=" + salary + ", age=" + age + "}";
    }
}

Problem 8.1: Sort Employees by Salary

public static List<Employee> sortBySalary(List<Employee> employees) {
    return employees.stream()
                    .sorted(Comparator.comparingDouble(Employee::getSalary))
                    .collect(Collectors.toList());
}

// Descending order
public static List<Employee> sortBySalaryDescending(List<Employee> employees) {
    return employees.stream()
                    .sorted(Comparator.comparingDouble(Employee::getSalary).reversed())
                    .collect(Collectors.toList());
}

Problem 8.2: Find Youngest Employee

public static Optional<Employee> findYoungestEmployee(List<Employee> employees) {
    return employees.stream()
                    .min(Comparator.comparingInt(Employee::getAge));
}

// Example usage
List<Employee> employees = Arrays.asList(
    new Employee(1, "Alice", "IT", 60000, 30),
    new Employee(2, "Bob", "HR", 50000, 25),
    new Employee(3, "Charlie", "IT", 70000, 35)
);
findYoungestEmployee(employees).ifPresent(e -> 
    System.out.println("Youngest: " + e.getName() + " (" + e.getAge() + ")")
);
// Output: Youngest: Bob (25)

Problem 8.3: Group by Department and Get Average Salary

public static Map<String, Double> averageSalaryByDept(List<Employee> employees) {
    return employees.stream()
                    .collect(Collectors.groupingBy(
                        Employee::getDepartment,
                        Collectors.averagingDouble(Employee::getSalary)
                    ));
}

// Example usage
List<Employee> employees = Arrays.asList(
    new Employee(1, "Alice", "IT", 60000, 30),
    new Employee(2, "Bob", "HR", 50000, 25),
    new Employee(3, "Charlie", "IT", 70000, 35),
    new Employee(4, "David", "HR", 55000, 28)
);
System.out.println(averageSalaryByDept(employees));
// Output: {IT=65000.0, HR=52500.0}

Problem 8.4: Get Employee Names Sorted by Age

public static List<String> getNamesSortedByAge(List<Employee> employees) {
    return employees.stream()
                    .sorted(Comparator.comparingInt(Employee::getAge))
                    .map(Employee::getName)
                    .collect(Collectors.toList());
}

// Example usage
System.out.println(getNamesSortedByAge(employees));
// Output: [Bob, Alice, David, Charlie]

Problem 8.5: Find Department with Highest Total Salary

public static Optional<Map.Entry<String, Double>> deptWithHighestTotalSalary(List<Employee> employees) {
    return employees.stream()
                    .collect(Collectors.groupingBy(
                        Employee::getDepartment,
                        Collectors.summingDouble(Employee::getSalary)
                    ))
                    .entrySet()
                    .stream()
                    .max(Map.Entry.comparingByValue());
}

// Example usage
deptWithHighestTotalSalary(employees).ifPresent(entry ->
    System.out.println("Department: " + entry.getKey() + ", Total: $" + entry.getValue())
);
// Output: Department: IT, Total: $130000.0

Problem 8.6: Increase Salary by 10% Using Map

public static List<Employee> increaseSalaryBy10Percent(List<Employee> employees) {
    return employees.stream()
                    .map(emp -> {
                        Employee newEmp = new Employee(
                            emp.getId(),
                            emp.getName(),
                            emp.getDepartment(),
                            emp.getSalary() * 1.10,
                            emp.getAge()
                        );
                        return newEmp;
                    })
                    .collect(Collectors.toList());
}

// Or modify in place (if mutable)
public static void increaseSalaryBy10PercentInPlace(List<Employee> employees) {
    employees.forEach(emp -> emp.setSalary(emp.getSalary() * 1.10));
}

9. Parallel Streams

Concepts

  • parallelStream() - Create parallel stream
  • Performance considerations
  • Thread safety concerns

Problem 9.1: Convert Stream to Parallel

public static long sumParallel(List<Integer> numbers) {
    return numbers.parallelStream()
                  .mapToLong(Integer::longValue)
                  .sum();
}

// Example usage
List<Integer> largeList = IntStream.rangeClosed(1, 1000000)
                                   .boxed()
                                   .collect(Collectors.toList());

long start = System.currentTimeMillis();
long sum = sumParallel(largeList);
long end = System.currentTimeMillis();
System.out.println("Sum: " + sum + ", Time: " + (end - start) + "ms");

Problem 9.2: Performance Comparison

public class ParallelStreamPerformance {
    
    public static long sequentialProcessing(List<Integer> numbers) {
        long start = System.currentTimeMillis();
        long sum = numbers.stream()
                         .mapToLong(n -> {
                             // Simulate some work
                             return (long) Math.sqrt(n);
                         })
                         .sum();
        long end = System.currentTimeMillis();
        System.out.println("Sequential time: " + (end - start) + "ms");
        return sum;
    }
    
    public static long parallelProcessing(List<Integer> numbers) {
        long start = System.currentTimeMillis();
        long sum = numbers.parallelStream()
                         .mapToLong(n -> {
                             // Simulate some work
                             return (long) Math.sqrt(n);
                         })
                         .sum();
        long end = System.currentTimeMillis();
        System.out.println("Parallel time: " + (end - start) + "ms");
        return sum;
    }
    
    public static void main(String[] args) {
        List<Integer> numbers = IntStream.rangeClosed(1, 1000000)
                                        .boxed()
                                        .collect(Collectors.toList());
        
        sequentialProcessing(numbers);
        parallelProcessing(numbers);
    }
}

When NOT to Use Parallel Streams

// ❌ BAD: Small datasets
List<Integer> smallList = Arrays.asList(1, 2, 3, 4, 5);
smallList.parallelStream().forEach(System.out::println); // Overhead > benefit

// ❌ BAD: Operations that must be ordered
List<String> words = Arrays.asList("a", "b", "c");
words.parallelStream().forEachOrdered(System.out::println); // Defeats purpose

// ❌ BAD: Stateful operations with shared mutable state
List<Integer> results = new ArrayList<>(); // NOT thread-safe
numbers.parallelStream()
       .forEach(n -> results.add(n * 2)); // Race condition!

// ✅ GOOD: Use thread-safe collection
List<Integer> results = new ConcurrentLinkedQueue<>();
numbers.parallelStream()
       .forEach(n -> results.add(n * 2));

// ✅ BETTER: Use collect
List<Integer> results = numbers.parallelStream()
                              .map(n -> n * 2)
                              .collect(Collectors.toList());

10. Tricky Conceptual Questions

Q1: Difference between map() and flatMap()

// map() - One-to-one transformation
List<String> words = Arrays.asList("Hello", "World");
List<Integer> lengths = words.stream()
                             .map(String::length)
                             .collect(Collectors.toList());
// Result: [5, 5]

// flatMap() - One-to-many transformation (flattening)
List<String> sentences = Arrays.asList("Hello World", "Java Streams");
List<String> allWords = sentences.stream()
                                 .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
                                 .collect(Collectors.toList());
// Result: [Hello, World, Java, Streams]

// Another example: Nested lists
List<List<Integer>> nested = Arrays.asList(
    Arrays.asList(1, 2),
    Arrays.asList(3, 4),
    Arrays.asList(5, 6)
);

// Using map - returns Stream<List<Integer>>
List<List<Integer>> withMap = nested.stream()
                                    .map(list -> list)
                                    .collect(Collectors.toList());
// Result: [[1, 2], [3, 4], [5, 6]]

// Using flatMap - returns Stream<Integer>
List<Integer> withFlatMap = nested.stream()
                                  .flatMap(List::stream)
                                  .collect(Collectors.toList());
// Result: [1, 2, 3, 4, 5, 6]

Q2: Difference between reduce() and collect()

// reduce() - Combines elements to produce single result (Optional or value)
// Immutability focused, returns Optional for empty streams
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

Optional<Integer> sum = numbers.stream()
                               .reduce((a, b) -> a + b);
// OR
int sum = numbers.stream()
                 .reduce(0, (a, b) -> a + b);

// collect() - Mutable reduction, accumulates into container
// More flexible, can create any collection type
List<Integer> evenNumbers = numbers.stream()
                                   .filter(n -> n % 2 == 0)
                                   .collect(Collectors.toList());

// Key differences:
// 1. reduce() creates new value each time (immutable)
// 2. collect() modifies existing container (mutable)
// 3. collect() is better for building collections
// 4. reduce() is better for combining values

Q3: Why Streams Cannot Be Reused?

// ❌ This will throw IllegalStateException
Stream<String> stream = Arrays.asList("a", "b", "c").stream();
stream.forEach(System.out::println);
stream.forEach(System.out::println); // IllegalStateException: stream has already been operated upon

// ✅ Solution: Create new stream each time
List<String> list = Arrays.asList("a", "b", "c");
list.stream().forEach(System.out::println);
list.stream().forEach(System.out::println); // Works fine

// Reason: Streams are designed for one-time use to optimize performance

Q4: Stateless vs Stateful Operations

// STATELESS operations - Don't depend on previous elements
// Examples: filter(), map(), flatMap()
numbers.stream()
       .filter(n -> n > 5)  // Each element processed independently
       .map(n -> n * 2)     // No dependency on other elements
       .collect(Collectors.toList());

// STATEFUL operations - Depend on previous elements
// Examples: sorted(), distinct(), limit(), skip()
numbers.stream()
       .distinct()  // Needs to remember what was seen
       .sorted()    // Needs to see all elements
       .limit(5)    // Needs to count
       .collect(Collectors.toList());

// Stateful operations are barriers to parallelization

Q5: Intermediate vs Terminal Operations

// INTERMEDIATE operations - Return a Stream, lazy evaluation
// filter(), map(), flatMap(), distinct(), sorted(), peek(), limit(), skip()
Stream<Integer> intermediate = numbers.stream()
                                      .filter(n -> n > 5)  // Intermediate
                                      .map(n -> n * 2);    // Intermediate
// Nothing happens yet!

// TERMINAL operations - Trigger execution, return non-Stream result
// forEach(), collect(), reduce(), count(), anyMatch(), allMatch(), 
// noneMatch(), findFirst(), findAny(), min(), max()
long count = numbers.stream()
                    .filter(n -> n > 5)
                    .count();  // Terminal - execution happens now!

// Multiple intermediate operations can be chained, but only ONE terminal operation

Q6: Lazy Evaluation in Streams

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

// Intermediate operations are not executed until terminal operation is called
Stream<Integer> stream = numbers.stream()
    .filter(n -> {
        System.out.println("Filtering: " + n);
        return n > 2;
    })
    .map(n -> {
        System.out.println("Mapping: " + n);
        return n * 2;
    });

System.out.println("Stream created, but nothing printed yet!");

// Now terminal operation triggers execution
List<Integer> result = stream.collect(Collectors.toList());

// Output:
// Stream created, but nothing printed yet!
// Filtering: 1
// Filtering: 2
// Filtering: 3
// Mapping: 3
// Filtering: 4
// Mapping: 4
// Filtering: 5
// Mapping: 5

// Notice: Operations are fused - filter and map happen together for each element

Q7: Short-Circuiting Operations

// Short-circuiting operations can terminate early
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

// limit() - stops after n elements
List<Integer> first5 = numbers.stream()
                              .limit(5)
                              .collect(Collectors.toList());

// anyMatch() - stops at first match
boolean hasEven = numbers.stream()
                        .peek(n -> System.out.println("Checking: " + n))
                        .anyMatch(n -> n % 2 == 0);
// Output: Only "Checking: 1" and "Checking: 2" are printed

// findFirst() - stops after finding first element
Optional<Integer> first = numbers.stream()
                                 .filter(n -> n > 5)
                                 .findFirst();

11. 15 MUST-DO Interview Problems

Problem 11.1: Frequency of Each Element

public static Map<Integer, Long> elementFrequency(List<Integer> numbers) {
    return numbers.stream()
                  .collect(Collectors.groupingBy(
                      Function.identity(),
                      Collectors.counting()
                  ));
}

// Example
List<Integer> nums = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 4, 4, 4);
System.out.println(elementFrequency(nums));
// Output: {1=1, 2=2, 3=3, 4=4}

Problem 11.2: Second Highest Number

public static Optional<Integer> secondHighest(List<Integer> numbers) {
    return numbers.stream()
                  .distinct()
                  .sorted(Comparator.reverseOrder())
                  .skip(1)
                  .findFirst();
}

// Example
List<Integer> nums = Arrays.asList(5, 2, 8, 8, 1, 9);
System.out.println("Second highest: " + secondHighest(nums).orElse(-1));
// Output: Second highest: 8

Problem 11.3: Find Duplicates

public static Set<Integer> findDuplicates(List<Integer> numbers) {
    Set<Integer> seen = new HashSet<>();
    return numbers.stream()
                  .filter(n -> !seen.add(n))
                  .collect(Collectors.toSet());
}

// Example
List<Integer> nums = Arrays.asList(1, 2, 3, 2, 4, 5, 3, 6);
System.out.println("Duplicates: " + findDuplicates(nums));
// Output: Duplicates: [2, 3]

Problem 11.4: First Non-Repeating Character

public static Optional<Character> firstNonRepeating(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();
}

// Example
System.out.println("First non-repeating: " + 
                   firstNonRepeating("programming").orElse('X'));
// Output: First non-repeating: p

Problem 11.5: Group Employees by Department

public static Map<String, List<Employee>> groupByDepartment(List<Employee> employees) {
    return employees.stream()
                    .collect(Collectors.groupingBy(Employee::getDepartment));
}

Problem 11.6: Highest Salary Per Department

public static Map<String, Double> maxSalaryByDept(List<Employee> employees) {
    return employees.stream()
                    .collect(Collectors.groupingBy(
                        Employee::getDepartment,
                        Collectors.collectingAndThen(
                            Collectors.maxBy(Comparator.comparingDouble(Employee::getSalary)),
                            opt -> opt.map(Employee::getSalary).orElse(0.0)
                        )
                    ));
}

Problem 11.7: Sort Map by Value

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

// Example
Map<String, Integer> scores = new HashMap<>();
scores.put("Alice", 85);
scores.put("Bob", 92);
scores.put("Charlie", 78);
System.out.println(sortMapByValue(scores));
// Output: {Charlie=78, Alice=85, Bob=92}

Problem 11.8: Count Occurrences of Each Word

public static Map<String, Long> wordCount(String text) {
    return Arrays.stream(text.toLowerCase().split("\\s+"))
                 .collect(Collectors.groupingBy(
                     Function.identity(),
                     Collectors.counting()
                 ));
}

// Example
String text = "java is great java is powerful";
System.out.println(wordCount(text));
// Output: {java=2, is=2, great=1, powerful=1}

Problem 11.9: Partition Even/Odd

public static Map<Boolean, List<Integer>> partitionEvenOdd(List<Integer> numbers) {
    return numbers.stream()
                  .collect(Collectors.partitioningBy(n -> n % 2 == 0));
}

// Example
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6);
Map<Boolean, List<Integer>> partitioned = partitionEvenOdd(nums);
System.out.println("Even: " + partitioned.get(true));
System.out.println("Odd: " + partitioned.get(false));

Problem 11.10: Find Longest String

public static Optional<String> longestString(List<String> strings) {
    return strings.stream()
                  .max(Comparator.comparingInt(String::length));
}

// Example
List<String> words = Arrays.asList("Java", "JavaScript", "Python");
longestString(words).ifPresent(s -> System.out.println("Longest: " + s));
// Output: Longest: JavaScript

Problem 11.11: Merge Two Lists

public static List<Integer> mergeLists(List<Integer> list1, List<Integer> list2) {
    return Stream.concat(list1.stream(), list2.stream())
                 .distinct()
                 .sorted()
                 .collect(Collectors.toList());
}

// Example
List<Integer> l1 = Arrays.asList(1, 3, 5);
List<Integer> l2 = Arrays.asList(2, 3, 4);
System.out.println(mergeLists(l1, l2));
// Output: [1, 2, 3, 4, 5]

Problem 11.12: Convert List to Map with Duplicate Key Handling

public static Map<Integer, String> listToMap(List<Employee> employees) {
    return employees.stream()
                    .collect(Collectors.toMap(
                        Employee::getId,
                        Employee::getName,
                        (existing, replacement) -> existing  // Keep first occurrence
                    ));
}

Problem 11.13: Average Salary Per Department

public static Map<String, Double> avgSalaryByDept(List<Employee> employees) {
    return employees.stream()
                    .collect(Collectors.groupingBy(
                        Employee::getDepartment,
                        Collectors.averagingDouble(Employee::getSalary)
                    ));
}

Problem 11.14: Find Top N Elements

public static List<Integer> topNElements(List<Integer> numbers, int n) {
    return numbers.stream()
                  .distinct()
                  .sorted(Comparator.reverseOrder())
                  .limit(n)
                  .collect(Collectors.toList());
}

// Example
List<Integer> nums = Arrays.asList(5, 2, 8, 1, 9, 3, 7);
System.out.println("Top 3: " + topNElements(nums, 3));
// Output: Top 3: [9, 8, 7]

Problem 11.15: Remove Null Values from List

public static List<String> removeNulls(List<String> strings) {
    return strings.stream()
                  .filter(Objects::nonNull)
                  .collect(Collectors.toList());
}

// Example
List<String> words = Arrays.asList("Java", null, "Python", null, "JavaScript");
System.out.println(removeNulls(words));
// Output: [Java, Python, JavaScript]

Quick Reference: Stream Operations

Intermediate Operations (Lazy)

Operation Description Example
filter() Filter elements .filter(n -> n > 5)
map() Transform elements .map(String::toUpperCase)
flatMap() Flatten nested streams .flatMap(List::stream)
distinct() Remove duplicates .distinct()
sorted() Sort elements .sorted()
peek() Debug/side-effects .peek(System.out::println)
limit() Take first n .limit(10)
skip() Skip first n .skip(5)

Terminal Operations (Eager)

Operation Description Example
forEach() Iterate .forEach(System.out::println)
collect() Collect to collection .collect(Collectors.toList())
reduce() Reduce to single value .reduce(0, Integer::sum)
count() Count elements .count()
anyMatch() Check if any match .anyMatch(n -> n > 5)
allMatch() Check if all match .allMatch(n -> n > 0)
noneMatch() Check if none match .noneMatch(n -> n < 0)
findFirst() Get first element .findFirst()
findAny() Get any element .findAny()
min() Find minimum .min(Comparator.naturalOrder())
max() Find maximum .max(Comparator.naturalOrder())

Common Collectors

Collector Description Example
toList() Collect to List Collectors.toList()
toSet() Collect to Set Collectors.toSet()
toMap() Collect to Map Collectors.toMap(k, v)
joining() Join strings Collectors.joining(", ")
counting() Count elements Collectors.counting()
summingInt() Sum integers Collectors.summingInt(T::field)
averagingInt() Average integers Collectors.averagingInt(T::field)
groupingBy() Group elements Collectors.groupingBy(T::field)
partitioningBy() Partition by predicate Collectors.partitioningBy(predicate)

Performance Tips

  1. Use primitive streams when possible: IntStream, LongStream, DoubleStream avoid boxing overhead
  2. Short-circuit operations: Use limit(), findFirst(), anyMatch() to stop early
  3. Parallel streams carefully: Only for large datasets with CPU-intensive operations
  4. Avoid stateful operations in parallel: sorted(), distinct() in parallel streams
  5. Method references over lambdas: String::length is cleaner than s -> s.length()
  6. Collect once: Don't call collect() multiple times on same stream

Common Mistakes to Avoid

  1. ❌ Reusing streams
  2. ❌ Modifying source during stream operations
  3. ❌ Using parallel streams for small datasets
  4. ❌ Side effects in stream operations
  5. ❌ Not handling Optional properly
  6. ❌ Using streams when simple loop is clearer

Conclusion

Java Streams provide a powerful, functional approach to processing collections. Master these patterns and you'll be well-prepared for any Java interview! Practice these problems regularly to build muscle memory.

Remember: Streams are about declaring WHAT you want, not HOW to do it. Think declaratively!


Good luck with your Java interviews! 🚀

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