Here is a non-exhaustive list of fundamental Java8 questions that tests Java8 FP/Declarative style coding. Some of them are one-liners, and they can be solved with pre-Java8 syntax as well, but the idea is to solve it with Java8 syntax. In this post I may have used some of the new constructs like records(A preview feature in Java14 and officially stable feature in Java16), string.chars()(since Java9) and others.


Queries

  1. Take first 10 integers(1 to 10)and return the sum of the squares of the even numbers in the list.
  2. Given a list of strings,
    "Java", "C#", "Java", "Python", "C", "C++", "Python", "Java", "C++"
    

    print the 3 most frequently occurring words.

  3. Find the most frequently occurring character in a string "america"
  4. Given a list of strings,
    "global", "warming", "is", "real", "prob"
    

    return the list of strings that have exactly 4 characters.

  5. Consider an employee object with salary attribute, create a sample list of employees, calculate the average salary of the employees.
  6. Given a list of integers
    25, 36, 75, 60
    

    return the largest number that is less than the average of the numbers.

  7. Given a list of integers
    2, 3, 7, 8, 9, 7, 5, 3, 2, 8
    

    return a new list that contains unique elements from the given list.

  8. Assume an employee object with department attribute as String type, create a sample list of employees, return a list of departments that have at least 2 employees.
  9. Given a list of integers, return a new list that contains only the even numbers from the original list.
  10. reduction queries:
    • Solve with reduce func: Given a list of integers
      • reduce to sum
      • reduce to product
      • reduce to minimum
      • reduce to maximum
    • Given a list of strings, concatenate all strings
    • Given a list of strings, find the total number of characters in a list of strings
    • Find the longest string from the given list of strings. What if there is a tie? Get all the matching longest strings
    • Given a list of employees with salary attribute, find the sum of salaries of all the employees.

Syntax refresher and common patterns

  • java.util.Arrays.asList(T... a)
    • Returns a fixed size list backed by array
    • You cannot add or remove elements to this list - Attempt to do that would result in java.lang.UnsupportedOperationException
  • Note: java.util.Arrays.asList(T... a) works with wrappers and objects not primitives
    • Example:

      int[] arr = {1, 2, 3, 4};
      Arrays.asList(arr); // returns List<int[]>
      
      Integer[] arr = {1,2,3,4};
      Arrays.asList(arr); // returns List<Integer>
      
      Arrays.asList(1,2,3,4); // returns List<Integer>
      
  • Passing array of primitives like int[] to Arrays.asList() it returns List<int[]> as shown above. If in case we have to stream an int[] use java.util.Arrays.stream(int[]).
    • Example below

      int[] x = {1,2,3,4};
      List<Integer> ints = 
        java.util.Arrays.stream(x) // converts int[] to IntStream
        .boxed() // convert IntStream to Stream<Integer>
        .collect(Collectors.toList()); // Collect Stream<Integer> to List<Integer>
      
    • Below are the overloaded methods versions of java.util.Arrays.stream()

      java.util.stream.DoubleStream java.util.Arrays.stream(double[] array)
      java.util.stream.DoubleStream java.util.Arrays.stream(double[] array, 
                                                            int startInclusive, 
                                                            int endExclusive)
      java.util.stream.IntStream java.util.Arrays.stream(int[] array)
      java.util.stream.IntStream java.util.Arrays.stream(int[] array, 
                                                         int startInclusive, 
                                                         int endExclusive)
      java.util.stream.LongStream java.util.Arrays.stream(int[] array)
      java.util.stream.LongStream java.util.Arrays.stream(int[] array, 
                                                          int startInclusive, 
                                                          int endExclusive)
      java.util.stream.Stream<T> java.util.Arrays.stream(T[] array)
      - /**
         Used for custom types like
         record Employee(){}
         Employee[] emps = {new Employee(), new Employee()};
         Arrays.stream(emps); // returns java.util.stream.Stream<Employee>
        */
      

Solve all the below problems using Java8 FP/declarative style syntax


  1. Take first 10 integers(1 to 10)and return the sum of the squares of the even numbers in the list.

    • Assume the list is given
     // Assume this as given input list
     List<Integer> ints = Arrays.asList(1, 2, 3, 4);
     int sum =
         ints.stream()
             .filter(x -> x % 2 == 0)
             .map(elm -> elm * elm)
             .collect(Collectors.summingInt(a -> a));
     System.out.println(sum); // Prints 20
    
    • Alternatively, if we have to assume and initialize the input
     int sum =
         java.util.stream.IntStream.rangeClosed(1, 10)
             .boxed()
             .filter(x -> x % 2 == 0)
             .map(elm -> elm * elm)
             .collect(Collectors.summingInt(a -> a));
     System.out.println(sum); // Prints 220
    
    • Alternatively
     int result =
         IntStream.rangeClosed(1, 10)
             .boxed()
             .filter(i -> i % 2 == 0)
             .mapToInt(elm -> elm * elm)
             .sum();
     System.out.println(result);
    
    • Note: java.util.stream.IntStream returns primitive int - So we have to use boxed() to make it as wrapper.
    • java.util.stream.IntStream.rangeClosed(startInclusive, endInclusive)

    • java.util.stream.IntStream.range(startInclusive, endExclusive)

  2. Given a list of strings, print the 3 most frequently occurring words.

    List<String> strings =
       Arrays.asList("Java", "C#", "Java", 
                     "Python", "C", "C++", 
                     "Python", "Java", "C++");
     Map<String, Long> wordCount =
       strings
       .stream()
       .collect(
         Collectors.groupingBy(str -> str, Collectors.counting()));
     System.out.println(wordCount); // Prints {C#=1, Java=3, C++=2, C=1, Python=2}
    
     List<String> _3MostFrequentWords =
         wordCount
         .entrySet()
         .stream()
         .sorted(Comparator.comparing(entry -> entry.getValue(), 
                                     (v1, v2) -> v2.compareTo(v1)))
         .limit(3)
         .map(entry -> entry.getKey())
         .collect(Collectors.toList());
     System.out.println(_3MostFrequentWords); // Prints [Java, C++, Python]
    
  3. Find the most frequently occurring character in a string.

    String x = "america";
    
     Map<Character, Long> charCount =
         x.chars() // Since Java9 - returns IntStream
             .mapToObj(i -> (char) i)// returns  Stream<Character>
             .collect(Collectors.groupingBy(c -> c, Collectors.counting()));
     System.out.println(charCount);
    
     Character result =
         charCount.entrySet().stream()
             .sorted(
               // sort by val in reverse order
               Comparator.comparing(entry -> entry.getValue(), 
                                   (v1, v2) -> v2.compareTo(v1)))
             .limit(1)
             .map(entry -> entry.getKey())
             .findFirst()
             .orElseThrow();
    
     System.out.println(result);
    
  4. Given a list of strings, return the list of strings that have exactly 4 characters.

    List<String> strings = Arrays.asList("global", "warming", 
                                         "is", "real", "prob");
    
    • Simple solution

       List<String> result =
         strings
         .stream()
         .filter(x -> x.length() == 4)
         .collect(Collectors.toList());
       System.out.println(result); // Prints [real, prob]
      
    • Alternatively

         List<String> result =
             strings
                 .stream()
                 .collect(Collectors.groupingBy(str -> str.length()))
                 .entrySet()
                 .stream()
                 .filter(entry -> entry.getKey() == 4)
                 .map(entry -> entry.getValue())
                 .findFirst()
                 .orElseThrow();
         System.out.println(result); // Prints [real, prob]
      
  5. Consider an employee object with salary attribute, create a sample list of employees, calculate the average salary of the employees.

    record Employee(String name, double sal) {}
    
    List<Employee> employees = Arrays.asList(
                                new Employee("srk", 87.5), 
                                new Employee("hero", 99.9));
    double avgSalOfEmps = employees
                          .stream()
                          .mapToDouble(emp -> emp.sal) // returns DoubleStream
                          .average() // returns OptionalDouble
                          .getAsDouble();
    System.out.println(avgSalOfEmps); // Prints 93.7
    
  6. Given a list of integers, return the largest number that is less than the average of the numbers.

    List<Integer> ints = Arrays.asList(25, 36, 75, 60);
    double avg = ints
                 .stream()
                 .mapToInt(i -> i)
                 .average()
                 .getAsDouble();
    System.out.println(avg); // Prints 49.0
       
    Double result = ints
                    .stream()
                    .mapToDouble(i -> i)
                    .filter(num -> num < avg)
                    .max()
                    .getAsDouble();
    System.out.println(result.intValue()); // Prints 36
    
  7. Given a list of integers, return a new list that contains unique elements from the given list.

     List<Integer> ints = Arrays.asList(2, 3, 7, 8, 9, 7, 5, 3, 2, 8);
    
     List<Integer> unqiueElems =
         ints.stream()
             .collect(Collectors.groupingBy(i -> i, Collectors.counting()))
             .entrySet()
             .stream()
             .filter(entry -> entry.getValue() == 1)
             .map(entry -> entry.getKey())
             .collect(Collectors.toList());
     System.out.println(unqiueElems); // Prints [5,9]
    
  8. Assume an employee object with department attribute as String type, create a sample list of employees, return a list of departments that have at least 2 employees.

    record Employee(String name, String department) {}
    
     List<Employee> emps =
         Arrays.asList(
             new Employee("srk", "IT"),
             new Employee("pull", "IT"),
             new Employee("jenny", "Finance"),
             new Employee("katly", "HR"),
             new Employee("Sujany", "HR"));
    
     // Return list of departments that have at least 2 employees
     List<String> departments =
         emps.stream().collect(
                       Collectors.groupingBy(emp -> emp.department))
             .entrySet()
             .stream()
             .filter(entry -> entry.getValue().size() >= 2)
             .map(entry -> entry.getKey())
             .collect(Collectors.toList());
     System.out.println(departments); // Prints [HR, IT]
    
  9. Given a list of integers, return a new list that contains only the even numbers from the original list.

    List<Integer> result = java.util.stream.IntStream
                           .rangeClosed(1, 10)
                           .boxed()
                           .filter(x -> x % 2 == 0)
                           .collect(Collectors.toList());
     System.out.println(result); // Prints [2, 4, 6, 8, 10]
    

Solve the below using reduce function


  1. Given a list of integers
    • reduce to sum

      •  int result = java.util.stream.IntStream
                     .rangeClosed(1, 3) // returns IntStream
                     .reduce((a, b) -> a + b) // returns OptionalInt
                     .getAsInt();
         System.out.println(result); // Prints 6
        
      • int result = java.util.stream.IntStream
                     .rangeClosed(1, 3)
                     .reduce(0, (a, b) -> a + b);
        System.out.println(result); // Prints 6
        
    • reduce to product

      •  int result = java.util.stream.IntStream
                     .rangeClosed(1, 3)
                     .reduce((a, b) -> a * b) // returns OptionalInt
                     .getAsInt();
         System.out.println(result); // Prints 6
        
      • int result = java.util.stream.IntStream
                     .rangeClosed(1, 3)
                     .reduce(1, (a, b) -> a * b);
        System.out.println(result); // Prints 6
        
    • reduce to minimum

      •  int result = java.util.stream.IntStream
                      .rangeClosed(1, 3)
                      .reduce((a, b) -> Math.min(a, b)) // returns OptionalInt
                      .getAsInt();
         System.out.println(result); // Prints 1
        
      • int result = java.util.stream.IntStream
                     .rangeClosed(1, 3)
                     .reduce(1, (a, b) -> Math.min(a, b));
        System.out.println(result); // Prints 1
        
    • reduce to maximum

      •  int result = java.util.stream.IntStream
                      .rangeClosed(1, 3)
                      .reduce((a, b) -> Math.max(a, b)) // returns OptionalInt
                      .getAsInt();
         System.out.println(result); // Prints 3
        
      • int result = java.util.stream.IntStream
                     .rangeClosed(1, 3)
                     .reduce(1, (a, b) -> Math.max(a, b));
        System.out.println(result); // Prints 3
        
  2. Given a list of strings, concatenate all strings

    String[] strings = {"un", "ited"};
    String result = java.util.Arrays.stream(strings)
                    .collect(Collectors.joining());
    System.out.println(result); // Prints "united"
    
  3. Given a list of strings, find the total number of characters in a list of strings

    •  List<String> strings = Arrays.asList("united", "states", 
                                            "of", "america");
       int charCount = strings
                       .stream()
                       .mapToInt(str -> str.length())
                       .sum();
       System.out.println(charCount); // Prints 21
      
    •  List<String> strings = Arrays.asList("united", "states", 
                                            "of", "america");
       int charCount = strings
                       .stream()
                       .collect(Collectors.joining())
                       .toCharArray()
                       .length;
       System.out.println(charCount); // Prints 21
      
  4. Find the longest string from the given list of strings
    • Note: What if there is a tie? Get all the matching longest strings
    List<String> strings = Arrays.asList("united", "states", 
                                         "of", "america");
    List<String> result =
         strings
         .stream()
         .collect(Collectors.groupingBy(str -> str.length()))
         .entrySet()
         .stream()
         // .max(Comparator.comparing(entry -> entry.getKey()))
         // the above line also works
         .max(Comparator.comparingInt(entry -> entry.getKey()))
         .map(entry -> entry.getValue())
         .orElseThrow(); 
         // throws java.util.NoSuchElementException if No value is present
     System.out.println(result); // Prints [america]
    
    
  5. Given a list of employees with salary attribute, find the sum of salaries of all the employees.

    record Employee(String name, double sal) {}
    List<Employee> employees = Arrays.asList(new Employee("srk", 100), 
                                             new Employee("hero", 200));
    double sumOfSalaries = employees
                           .stream()
                           .mapToDouble(emp -> emp.sal)
                           .sum();
    System.out.println(sumOfSalaries); // Prints 300.0
    

SQL equivalents in Java8


Let’s consider a simple dataset of employee table with cols name, salary, and department

name salary department
Alice 50000 HR
Bob 70000 Finance
Charlie 60000 HR
Dave 90000 Finance
Steve 80000 HR
  1. SQL: Example of GROUP BY the employees by department and calculate the total salary for each department

    SELECT department, SUM(salary) FROM employees GROUP BY department;

    Java8

    List<Employee> emps = Arrays.asList(
         new Employee("Alice", 50000, "HR"),
         new Employee("Bob", 70000, "Finance"),
         new Employee("Charlie", 60000, "HR"),
         new Employee("Dave", 90000, "Finance"),
         new Employee("Steve", 80000, "HR")
       
    Map<String, Integer> totalSalariesByDept = 
                 employees
                 .stream()
                 .collect(Collectors.groupingBy(
                               Employee::getDepartment, 
                               Collectors.summingInt(Employee::getSalary)));
    )
    
  2. SQL

    SELECT * FROM employee WHERE salary > 5000 ORDER BY name ASC;

    Java8

    List<Employee> emps =
         employees
         .stream()
         .filter(emp -> emp.getSalary() > 5000)
         .sorted(Comparator.comparing(emp -> emp.getName()))
         .collect(Collectors.toList());
    
  3. SQL

    SELECT COUNT(*) FROM employee;

    Java8

    long count = employees
                 .stream()
                 .count();
    
  4. SQL

    SELECT DISTINCT department FROM employee;

    Java8

    List<String> departments = employees
                               .stream()
                               .map(emp -> emp.getDepartment())
                               .distinct() // removes duplicates
                               .collect(Collectors.toList());
    
  5. SQL

    SELECT MAX(salary) FROM employee;

    Java8

    double maxSal = employees
                    .stream()
                    .map(emp -> emp.getSalary())
                    .max()// returns OptionalDouble
                    .getAsDouble();
    
  6. SQL

    SELECT * FROM employee WHERE name LIKE ‘J%’;

    Java8

    List<Employee> filteredEmps = employees
                          .stream()
                          .filter(emp -> emp.getName().startsWith("J"))
                          .collect(Collectors.toList());
    
  7. SQL

    SELECT * FROM employee WHERE department IN (‘IT’, ‘Finance’);

    Java8

    List<Employee> filteredEmps = 
                   employees
                   .stream()
                   .filter(emp -> emp.getDepartment().equals("IT") 
                           || 
                           emp.getDepartment().equals("Finance"))
                   .collect(Collectors.toList());
    
  8. SQL

    SELECT name, salary FROM employee WHERE salary BETWEEN 40000 AND 60000;

    Java8

    Map<String, Double> nameSalMap = 
       employees
       .stream()
       .filter(emp -> emp.getSalary() >= 4000 && emp.getSalary() <= 6000)
       .collect(Collectors.toMap(emp -> emp.getName(), emp -> emp.getSalary()));
    
  9. SQL

    SELECT AVG(salary) FROM employee WHERE department = ‘Sales’;

    Java8

    double avgSal = employees
                     .stream()
                     .filter(emp -> emp.getDepartment().equals("Sales"))
                     .mapToDouble(emp -> emp.getSalary())
                     .average() // returns OptionalDouble
                     .getAsDouble();
    
  10. SQL

    SELECT * FROM employee WHERE hire_date >= ‘2022-01-01’ AND hire_date <= ‘2022-12-31’

    Java8

    List<Employee> emps = 
            employees
            .stream()
            .filter(
              emp -> emp.getHireDate().isAfter(LocalDate.of(2022,1,1)) 
              && 
              emp.getHireDate().isBefore(LocalDate.of(2022, 12, 31)))
            .collect(Collectors.toList())
    
  11. SQL

    SELECT COUNT(*) FROM employee GROUP BY department;

    Java8

    Map<String, Long> employeeCountByDepartment =
              employees
              .stream()
              .collect(
                  Collectors.groupingBy(
                    emp -> emp.getDepartment(), 
                    Collectors.counting()));