Header Ads Widget

Responsive Advertisement

Mastering Java 8 features

 

Mastering Java 8 features like Lambdas, Streams, and Collectors opens the door to elegant and efficient solutions for data processing. Parsing strings to create a Map<String, List<String>> using Java 8 lambdas is a great example, where Stream and Collectors help transform data into the desired format seamlessly. When working with maps, converting an unsorted Map to a sorted Map can be achieved effortlessly with lambda expressions and Collectors.toMap().

Sorting by multiple attributes is another powerful capability enabled by Java 8. Using the Comparator in streams or collections, you can chain comparisons to sort objects based on multiple criteria, such as sorting employees first by department and then by salary.

Aggregator methods in the Stream API, like count(), min(), max(), sum(), average(), and reduce(), simplify performing summary operations. These methods enhance readability while handling computations efficiently on data streams.

For complex aggregations, the combination of Collectors.groupingBy() and Collectors.counting() is invaluable. You can group objects like employees by a specific attribute, such as department, and perform operations like counting employees or summing salaries. Moreover, sorting grouped data by aggregated values, such as total salary per department, can be achieved using a combination of collectors and custom sorting logic.

Unlock Java 8’s potential to simplify and enhance your data processing capabilities. Whether transforming data, sorting, or performing complex aggregations, these techniques elevate your code efficiency and clarity.

Learn, explore, and master Java’s functional programming approach!


A.      To parse the string and create a Map<String, List<String>> using Java 8 lambdas, you can follow this approach:

Input Example:

String input = "2R:1R,2R|3R:3R|5R:4R,5R|10R:6R,7R,8R,9R,10R";

 

Output:

A map where the key is the part before the colon (:) and the value is a list of elements after splitting by commas (,).

Code:

java

import java.util.*;

import java.util.stream.*;

 

public class StringToMap {

    public static void main(String[] args) {

        String input = "2R:1R,2R|3R:3R|5R:4R,5R|10R:6R,7R,8R,9R,10R";

 

        // Split the string by '|' and process each segment

        Map<String, List<String>> result = Arrays.stream(input.split("\\|"))

            .map(part -> part.split(":")) // Split each part into key and value

            .collect(Collectors.toMap(

                arr -> arr[0], // The key (before ':')

                arr -> Arrays.asList(arr[1].split(",")) // The value (split by ',')

            ));

 

        // Print the resulting map

        result.forEach((key, value) ->

            System.out.println(key + " -> " + value)

        );

    }

}

 

Output:

2R -> [1R, 2R]

3R -> [3R]

5R -> [4R, 5R]

10R -> [6R, 7R, 8R, 9R, 10R]

 

Here’s how you can handle cases where the input string might be null or empty and still create a Map<String, List<String>> using Java 8 lambdas:

Code:

java

import java.util.*;

import java.util.stream.*;

 

public class StringToMap {

    public static void main(String[] args) {

        String input = null; // Example: input is null

        // String input = "2R:1R,2R|3R:3R|5R:4R,5R|10R:6R,7R,8R,9R,10R"; // Example: valid input

 

        // Initialize the map

        Map<String, List<String>> result = Optional.ofNullable(input) // Handle null input

            .filter(s -> !s.isEmpty()) // Filter out empty strings

            .map(data -> Arrays.stream(data.split("\\|")) // Split by '|'

                .map(part -> part.split(":")) // Split each part into key-value

                .collect(Collectors.toMap(

                    arr -> arr[0], // The key (before ':')

                    arr -> Arrays.asList(arr[1].split(",")) // The value (split by ',')

                ))

            ).orElseGet(HashMap::new); // If input is null or empty, return an empty map

 

        // Print the resulting map

        result.forEach((key, value) ->

            System.out.println(key + " -> " + value)

        );

    }

}

 

Output:

When Input is Valid:

For input "2R:1R,2R|3R:3R|5R:4R,5R|10R:6R,7R,8R,9R,10R", the output will be:

2R -> [1R, 2R]

3R -> [3R]

5R -> [4R, 5R]

10R -> [6R, 7R, 8R, 9R, 10R]

When Input is null or Empty:

For input = null or input = "", the output will be:

(empty map)

 

Here’s how you can handle null or non-null input data and create an ArrayList<String> using Java 8 lambdas:

Code:

java

import java.util.*;

import java.util.stream.*;

 

public class StringToArrayList {

    public static void main(String[] args) {

        String input = null; // Example: input is null

        // String input = "1R,2R,3R,4R,5R"; // Example: valid input

 

        // Create ArrayList based on input

        List<String> result = Optional.ofNullable(input) // Handle null input

            .filter(s -> !s.isEmpty()) // Ensure input is not empty

            .map(data -> Arrays.stream(data.split(",")) // Split by comma

                .collect(Collectors.toList()) // Collect into a List

            ).orElseGet(ArrayList::new); // If input is null or empty, return an empty list

 

        // Print the resulting list

        System.out.println(result);

    }

}

 

Output:

When Input is Valid:

For input = "1R,2R,3R,4R,5R", the output will be:

[1R, 2R, 3R, 4R, 5R]

 

When Input is null or Empty:

For input = null or input = "", the output will be:

[]

 

Here's how you can handle a null input, or create an ArrayList from a List derived by splitting the input string using Java 8 lambdas:

Code:

java

import java.util.*;

import java.util.stream.Collectors;

 

public class Main {

    public static void main(String[] args) {

        String input = null; // Example: input is null

        // String input = "1R,2R,3R,4R,5R"; // Example: valid input

 

        // Create ArrayList based on input

        ArrayList<String> result = Optional.ofNullable(input) // Handle null input

            .filter(s -> !s.isEmpty()) // Ensure input is not empty

            .map(data -> Arrays.stream(data.split(",")) // Split by comma

                .collect(Collectors.toCollection(ArrayList::new)) // Collect into an ArrayList

            ).orElseGet(ArrayList::new); // If input is null or empty, return a new ArrayList

 

        // Print the resulting ArrayList

        System.out.println(result);

    }

}

 

Output:

When Input is Valid:

For input = "1R,2R,3R,4R,5R", the output will be:

[1R, 2R, 3R, 4R, 5R]

 

When Input is null or Empty:

For input = null or input = "", the output will be:

[]

 


 

B.      In Java 8, you can use Stream and Collectors to convert an unsorted Map to a sorted Map using a lambda expression. Below is an example of how you can do this:

Example: Sorting by Keys

java

import java.util.*;

import java.util.stream.Collectors;

 

public class MapSortingExample {

    public static void main(String[] args) {

        // Unsorted Map

        Map<String, Integer> unsortedMap = new HashMap<>();

        unsortedMap.put("Banana", 3);

        unsortedMap.put("Apple", 5);

        unsortedMap.put("Cherry", 1);

        unsortedMap.put("Mango", 2);

 

        // Sorted Map by Keys

        Map<String, Integer> sortedByKey = unsortedMap.entrySet()

                .stream()

                .sorted(Map.Entry.comparingByKey())

                .collect(Collectors.toMap(

                        Map.Entry::getKey,

                        Map.Entry::getValue,

                        (oldValue, newValue) -> oldValue,

                        LinkedHashMap::new

                ));

 

        System.out.println("Sorted by Keys: " + sortedByKey);

    }

}

 

Example: Sorting by Values

java

import java.util.*;

import java.util.stream.Collectors;

 

public class MapSortingExample {

    public static void main(String[] args) {

        // Unsorted populated Map

        Map<String, Integer> unsortedMap = new HashMap<>();

        unsortedMap.put("Banana", 3);

        unsortedMap.put("Apple", 5);

        unsortedMap.put("Cherry", 1);

        unsortedMap.put("Mango", 2);

 

        // Sorted populated Map by Values

        Map<String, Integer> sortedByValue = unsortedMap.entrySet()

                .stream()

                .sorted(Map.Entry.comparingByValue())

                .collect(Collectors.toMap(

                        Map.Entry::getKey,

                        Map.Entry::getValue,

                        (oldValue, newValue) -> oldValue,

                        LinkedHashMap::new

                ));

 

        System.out.println("Sorted by Values: " + sortedByValue);

    }

}

 

Explanation:

  1. entrySet().stream(): Converts the map's entry set into a stream of key-value pairs.
  2. sorted(): Sorts the stream based on a comparator. Use Map.Entry.comparingByKey() or Map.Entry.comparingByValue() for sorting by keys or values, respectively.
  3. Collectors.toMap(): Collects the sorted stream into a new Map.
    • Map.Entry::getKey: Extracts the key.
    • Map.Entry::getValue: Extracts the value.
    • (oldValue, newValue) -> oldValue: Handles duplicate keys (not usually needed if the map is valid).
    • LinkedHashMap::new: Ensures the map maintains insertion order (important for sorted results).

Output:

For the first example:

Sorted by Keys: {Apple=5, Banana=3, Cherry=1, Mango=2}

 

For the second example:

Sorted by Values: {Cherry=1, Mango=2, Banana=3, Apple=5}

 

 

 


 

C.      Sorting by multiple attributes in Java can be done using Comparator with streams or collections. Below are examples of sorting by multiple attributes using both a Map and a List.


1. Sorting a List of Objects by Multiple Attributes

Suppose you have a StaffMem class with name, age, and salary attributes, and you want to sort by multiple attributes.

Example Code:

java

import java.util.*;

import java.util.stream.Collectors;

 

class StaffMem {

    private String name;

    private int age;

    private double salary;

 

    public StaffMem(String name, int age, double salary) {

        this.name = name;

        this.age = age;

        this.salary = salary;

    }

 

    public String getName() {

        return name;

    }

 

    public int getAge() {

        return age;

    }

 

    public double getSalary() {

        return salary;

    }

 

    @Override

    public String toString() {

        return " StaffMem{name='" + name + "', age=" + age + ", salary=" + salary + '}';

    }

}

 

public class MultiAttributeSortingList {

    public static void main(String[] args) {

        List<StaffMem> people = Arrays.asList(

                new StaffMem("Alice", 30, 60000),

                new StaffMem("Bob", 25, 70000),

                new StaffMem("Alice", 35, 50000),

                new StaffMem("Charlie", 30, 60000)

        );

 

        // Sort by name, then age, then salary

        List<StaffMem> sortedPeople = people.stream()

                .sorted(Comparator.comparing(StaffMem::getName)

                        .thenComparing(StaffMem::getAge)

                        .thenComparing(StaffMem::getSalary))

                .collect(Collectors.toList());

 

        sortedPeople.forEach(System.out::println);

}

}

 

Output:

StaffMem{name='Alice', age=30, salary=60000.0}

StaffMem{name='Alice', age=35, salary=50000.0}

StaffMem{name='Bob', age=25, salary=70000.0}

StaffMem{name='Charlie', age=30, salary=60000.0}

 


2. Sorting a Map by Multiple Attributes

If you have a Map where the key is an object (e.g., StaffMem) or the value contains multiple attributes, you can sort it similarly using streams.

Example: Sorting by Map Values (Custom Object)

java

import java.util.*;

import java.util.stream.Collectors;

 

public class MultiAttributeSortingMap {

    public static void main(String[] args) {

        Map<Integer, StaffMem> personMap = new HashMap<>();

        personMap.put(1, new StaffMem("Alice", 30, 60000));

        personMap.put(2, new StaffMem("Bob", 25, 70000));

        personMap.put(3, new StaffMem("Alice", 35, 50000));

        personMap.put(4, new StaffMem("Charlie", 30, 60000));

 

        // Sort by StaffMem attributes in the map values

        Map<Integer, StaffMem> sortedMap = personMap.entrySet()

                .stream()

                .sorted(Map.Entry.<Integer, StaffMem>comparingByValue(

                        Comparator.comparing(StaffMem::getName)

                                .thenComparing(StaffMem::getAge)

                                .thenComparing(StaffMem::getSalary)

                ))

                .collect(Collectors.toMap(

                        Map.Entry::getKey,

                        Map.Entry::getValue,

                        (oldValue, newValue) -> oldValue,

                        LinkedHashMap::new

                ));

 

        sortedMap.forEach((key, value) -> System.out.println(key + " -> " + value));

    }

}

 

Output:

1 -> StaffMem{name='Alice', age=30, salary=60000.0}

3 -> StaffMem{name='Alice', age=35, salary=50000.0}

2 -> StaffMem{name='Bob', age=25, salary=70000.0}

4 -> StaffMem{name='Charlie', age=30, salary=60000.0}

 


3. Sorting a List of Maps by Multiple Attributes

If you have a list of maps, you can sort by multiple attributes in the map values.

Example Code:

java

import java.util.*;

import java.util.stream.Collectors;

 

public class MultiAttributeSortingListOfMaps {

    public static void main(String[] args) {

        List<Map<String, Object>> records = new ArrayList<>();

        records.add(Map.of("name", "Alice", "age", 30, "salary", 60000));

        records.add(Map.of("name", "Bob", "age", 25, "salary", 70000));

        records.add(Map.of("name", "Alice", "age", 35, "salary", 50000));

        records.add(Map.of("name", "Charlie", "age", 30, "salary", 60000));

 

        // Sort by name, then age, then salary

        List<Map<String, Object>> sortedRecords = records.stream()

                .sorted(Comparator.comparing((Map<String, Object> map) -> (String) map.get("name"))

                        .thenComparing(map -> (Integer) map.get("age"))

                        .thenComparing(map -> (Double) map.get("salary")))

                .collect(Collectors.toList());

 

        sortedRecords.forEach(System.out::println);

    }

}

 

Output:

{name=Alice, age=30, salary=60000.0}

{name=Alice, age=35, salary=50000.0}

{name=Bob, age=25, salary=70000.0}

{name=Charlie, age=30, salary=60000.0}

 


Key Concepts:

  1. Comparator.comparing: Used to compare objects based on a specific attribute.
  2. thenComparing: Chains multiple comparisons for additional attributes.
  3. Stream API: Allows sorting with custom logic using lambdas.
  4. LinkedHashMap: Ensures the insertion order is preserved when collecting sorted results.

Example: Generic Multi-Attribute Sorting

import java.util.*;

import java.util.function.Function;

import java.util.stream.Collectors;

 

class StaffMem {

    private String name;

    private int age;

    private double salary;

 

    public StaffMem(String name, int age, double salary) {

        this.name = name;

        this.age = age;

        this.salary = salary;

    }

 

    public String getName() {

        return name;

    }

 

    public int getAge() {

        return age;

    }

 

    public double getSalary() {

        return salary;

    }

 

    @Override

    public String toString() {

        return String.format("StaffMem{name='%s', age=%d, salary=%.2f}", name, age, salary);

    }

}

 

public class MultiAttributeSort {

    public static void main(String[] args) {

        List<StaffMem> employees = Arrays.asList(

                new StaffMem("Alice", 30, 60000),

                new StaffMem("Bob", 25, 70000),

                new StaffMem("Charlie", 25, 80000),

                new StaffMem("David", 35, 50000),

                new StaffMem("Eve", 30, 90000)

        );

 

        // Sort employees by age (ascending) and salary (descending)

        List<StaffMem> sortedEmployees = sortByMultipleAttributes(

                employees,

                Comparator.comparing(StaffMem::getAge) // First attribute: Age

                        .thenComparing(Comparator.comparing(StaffMem::getSalary).reversed()) // Second attribute: Salary

        );

 

        // Print sorted employees

        sortedEmployees.forEach(System.out::println);

    }

 

    // Generic method for sorting by multiple attributes

    public static <T> List<T> sortByMultipleAttributes(List<T> list, Comparator<T> comparator) {

        return list.stream()

                .sorted(comparator)

                .collect(Collectors.toList());

    }

}

 

 

Explanation:

  1. Generic Method:
    • sortByMultipleAttributes(List<T> list, Comparator<T> comparator):
      • Accepts a list of any type T.
      • Accepts a Comparator<T> to define the sorting logic.
  2. Flexible Comparator:
    • Use Comparator.comparing() for the primary attribute.
    • Chain multiple sorting criteria using thenComparing().
    • Use .reversed() to sort in descending order.
  3. Reusability:
    • The method can be reused for any object type (T) and any combination of attributes.

Output:

StaffMem{name='Bob', age=25, salary=70000.00}

StaffMem{name='Charlie', age=25, salary=80000.00}

StaffMem{name='Alice', age=30, salary=60000.00}

StaffMem{name='Eve', age=30, salary=90000.00}

StaffMem{name='David', age=35, salary=50000.00}

 


How to Use:

  • Pass a custom Comparator based on the attributes you want to sort.
  • The method will work for any type of object, not just Employee.

Sorting with Dynamic Attribute Selection:

If you want to make it even more dynamic (e.g., select attributes at runtime), you can use a list of functions (List<Function<T, ?>>) to define sorting keys. Let me know if you'd like to see this variation!

 


 

D.     In Java 8, aggregator methods in the Stream API are used to perform summary operations such as counting, finding minimum/maximum, summing, averaging, or reducing. These methods can be applied to streams and include operations like count(), min(), max(), sum(), average(), and reduce().

Here’s an example of each aggregator method with Java 8 and lambda expressions:


1. Count

Counts the number of elements in a stream.

java

import java.util.Arrays;

import java.util.List;

 

public class AggregatorExample {

    public static void main(String[] args) {

        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

 

        long count = names.stream().count(); // Count elements

        System.out.println("Count: " + count);

    }

}

 

Output:

Count: 4

 


2. Min

Finds the minimum value in the stream.

java

import java.util.Arrays;

import java.util.Optional;

 

public class AggregatorExample {

    public static void main(String[] args) {

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

 

        Optional<Integer> min = numbers.stream().min(Integer::compareTo); // Find the smallest number

        min.ifPresent(value -> System.out.println("Min: " + value));

    }

}

 

Output:

Min: 2

 


3. Max

Finds the maximum value in the stream.

java

import java.util.Arrays;

import java.util.Optional;

 

public class AggregatorExample {

    public static void main(String[] args) {

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

 

        Optional<Integer> max = numbers.stream().max(Integer::compareTo); // Find the largest number

        max.ifPresent(value -> System.out.println("Max: " + value));

    }

}

 

Output:

Max: 9

 


4. Sum

Calculates the sum of numeric values.

java

import java.util.Arrays;

 

public class AggregatorExample {

    public static void main(String[] args) {

        int[] numbers = {3, 9, 2, 8, 4};

 

        int sum = Arrays.stream(numbers).sum(); // Sum up all numbers

        System.out.println("Sum: " + sum);

    }

}

 

Output:

Sum: 26

 


5. Average

Calculates the average of numeric values.

java

import java.util.Arrays;

import java.util.OptionalDouble;

 

public class AggregatorExample {

    public static void main(String[] args) {

        int[] numbers = {3, 9, 2, 8, 4};

 

        OptionalDouble average = Arrays.stream(numbers).average(); // Calculate average

        average.ifPresent(value -> System.out.println("Average: " + value));

    }

}

 

Output:

Average: 5.2

 


6. Reduce

Performs a reduction operation, combining elements to produce a single result.

java

import java.util.Arrays;

import java.util.List;

import java.util.Optional;

 

public class AggregatorExample {

    public static void main(String[] args) {

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

 

        // Sum all numbers using reduce

        Optional<Integer> reducedSum = numbers.stream().reduce((a, b) -> a + b);

        reducedSum.ifPresent(value -> System.out.println("Reduced Sum: " + value));

    }

}

 

Output:

Reduced Sum: 26

 


7. Collectors (Bonus: Aggregate Results)

Use Collectors to aggregate results into collections or summaries.

Example: Group and count occurrences of each word.

java

import java.util.Arrays;

import java.util.List;

import java.util.Map;

import java.util.stream.Collectors;

 

public class AggregatorExample {

    public static void main(String[] args) {

        List<String> words = Arrays.asList("apple", "banana", "apple", "orange", "banana", "apple");

 

        Map<String, Long> wordCount = words.stream()

                .collect(Collectors.groupingBy(word -> word, Collectors.counting()));

 

        System.out.println("Word Count: " + wordCount);

    }

}

 

Output:

Word Count: {banana=2, orange=1, apple=3}

 


Summary of Aggregators:

Ø  count() → Count elements.

Ø  min() → Find the minimum.

Ø  max() → Find the maximum.

Ø  sum() → Sum all elements (from numeric streams).

Ø  average() → Calculate the average (from numeric streams).

Ø  reduce() → Combine elements to produce a single result.

Ø  Collectors.groupingBy() and Collectors.counting() → For complex aggregations.

 


E.      Here’s an example of how to use Collectors.groupingBy(), Collectors.counting(), and Collectors in conjunction with orderBy logic for complex aggregations with an Employee object.


Example Scenario

Suppose we have a list of Employee objects with attributes:

Ø  id

Ø  name

Ø  department

Ø  salary.

We will perform the following complex aggregations:

  1. Group employees by department and count how many employees are in each department.
  2. Group employees by department and find the total salary in each department.
  3. Group employees by department, sort them by total salary (descending order).
  4. Perform a group by department and order by salary for employees.

Java Implementation

java

import java.util.*;

import java.util.stream.Collectors;

 

class KamicalEmployee {

    private int id;

    private String name;

    private String department;

    private double salary;

 

    // Constructor

    public KamicalEmployee(int id, String name, String department, double salary) {

        this.id = id;

        this.name = name;

        this.department = department;

        this.salary = salary;

    }

 

    // Getters

    public int getId() {

        return id;

    }

    public String getName() {

        return name;

    }

    public String getDepartment() {

        return department;

    }

 

    public double getSalary() {

        return salary;

    }

    @Override

    public String toString() {

        return "KamicalEmployee{id=" + id + ", name='" + name + "', department='" + department + "', salary=" + salary + '}';

    }

}

 

public class KamicalEmployeeAggregationExample {

    public static void main(String[] args) {

        List<KamicalEmployee> employees = Arrays.asList(

            new KamicalEmployee(1, "Alice", "HR", 3000),

            new KamicalEmployee(2, "Bob", "IT", 4000),

            new KamicalEmployee(3, "Charlie", "IT", 4500),

            new KamicalEmployee(4, "David", "HR", 3200),

            new KamicalEmployee(5, "Eve", "Finance", 5000),

            new KamicalEmployee(6, "Frank", "Finance", 5500),

            new KamicalEmployee(7, "Grace", "IT", 4700)

        );

 

        // 1. Organize employees by department and tally the number of individuals in each department.

        Map<String, Long> employeeCountByDepartment = employees.stream()

            .collect(Collectors.groupingBy(KamicalEmployee::getDepartment, Collectors.counting()));

 

        System.out.println("Employee Count by Department:");

        employeeCountByDepartment.forEach((department, count) ->

            System.out.println(department + ": " + count));

 

        // 2. Categorize by department and calculate the aggregate salary for each department.

        Map<String, Double> totalSalaryByDepartment = employees.stream()

            .collect(Collectors.groupingBy(KamicalEmployee::getDepartment, Collectors.summingDouble(KamicalEmployee::getSalary)));

 

        System.out.println("\nTotal Salary by Department:");

        totalSalaryByDepartment.forEach((department, totalSalary) ->

            System.out.println(department + ": " + totalSalary));

 

        // 3. Classify by department and arrange the departments based on total salary in descending order.

        LinkedHashMap<String, Double> sortedDepartmentsBySalary = employees.stream()

            .collect(Collectors.groupingBy(KamicalEmployee::getDepartment, Collectors.summingDouble(KamicalEmployee::getSalary)))

            .entrySet()

            .stream()

            .sorted((e1, e2) -> Double.compare(e2.getValue(), e1.getValue())) // Sort by total salary (descending)

            .collect(Collectors.toMap(

                Map.Entry::getKey,

                Map.Entry::getValue,

                (oldValue, newValue) -> oldValue,

                LinkedHashMap::new // Maintain insertion order

            ));

 

        System.out.println("\nDepartments Sorted by Total Salary:");

        sortedDepartmentsBySalary.forEach((department, totalSalary) ->

            System.out.println(department + ": " + totalSalary));

 

        // 4. Sort employees within each department by salary while grouping by department.

        Map<String, List<KamicalEmployee>> employeesGroupedAndSorted = employees.stream()

            .collect(Collectors.groupingBy(

                KamicalEmployee::getDepartment,

                Collectors.collectingAndThen(

                    Collectors.toList(),

                    list -> list.stream()

                        .sorted(Comparator.comparingDouble(KamicalEmployee::getSalary).reversed()) // Sorting order by salary descending

                        .collect(Collectors.toList())

                )

            ));

 

        System.out.println("\nEmployees Grouped by Department and Sorted by Salary:");

        employeesGroupedAndSorted.forEach((department, employeeList) -> {

            System.out.println(department + ": ");

            employeeList.forEach(System.out::println);

        });

    }

}

 


Output

  1. Employee Count by Department:

output

Employee Count by Department:

HR: 2

IT: 3

Finance: 2

 

  1. Total Salary by Department:

output

Total Salary by Department:

HR: 6200.0

IT: 13200.0

Finance: 10500.0

 

  1. Departments Sorted by Total Salary:

output

Departments Sorted by Total Salary:

IT: 13200.0

Finance: 10500.0

HR: 6200.0

 

  1. Employees Grouped by Department and Sorted by Salary:

output

Employees Grouped by Department and Sorted by Salary:

HR:

KamicalEmployee {id=4, name='David', department='HR', salary=3200.0}

KamicalEmployee {id=1, name='Alice', department='HR', salary=3000.0}

IT:

KamicalEmployee {id=7, name='Grace', department='IT', salary=4700.0}

KamicalEmployee {id=3, name='Charlie', department='IT', salary=4500.0}

KamicalEmployee {id=2, name='Bob', department='IT', salary=4000.0}

Finance:

KamicalEmployee {id=6, name='Frank', department='Finance', salary=5500.0}

KamicalEmployee {id=5, name='Eve', department='Finance', salary=5000.0}

 


Explanation:

  1. groupingBy() groups employees by department.
  2. counting() counts employees in each department.
  3. summingDouble() calculates the total salary for each department.
  4. Sorting:
    • Departments are sorted by total salary using sorted().
    • Employees within each department are sorted by salary using Comparator.
  5. LinkedHashMap is used to maintain the insertion order of sorted data.

 


 

F.       To group by multiple attributes in Java 8 using Collectors.groupingBy(), you can create a composite key (e.g., by combining multiple attributes) or use nested Collectors.groupingBy() calls. Here's an example using a Staff object with department and designation attributes:

Example: Grouping by Multiple Attributes

java

import java.util.*;

import java.util.stream.Collectors;

 

class Staff {

    private String name;

    private String department;

    private String designation;

    private double salary;

 

    public Staff(String name, String department, String designation, double salary) {

        this.name = name;

        this.department = department;

        this.designation = designation;

        this.salary = salary;

    }

 

    public String getDepartment() {

        return department;

    }

 

    public String getDesignation() {

        return designation;

    }

 

    public double getSalary() {

        return salary;

    }

 

    public String getName() {

        return name;

    }

 

    @Override

    public String toString() {

        return String.format("Staff{name='%s', department='%s', designation='%s', salary=%.2f}",

                name, department, designation, salary);

    }

}

 

public class GroupByMultipleAttributes {

    public static void main(String[] args) {

        List<Staff> staffList = Arrays.asList(

            new Staff("Alice", "IT", "Developer", 60000),

            new Staff("Bob", "IT", "Developer", 70000),

            new Staff("Charlie", "HR", "Manager", 80000),

            new Staff("David", "HR", "Executive", 50000),

            new Staff("Eve", "IT", "Manager", 90000)

        );

 

        // Grouping by department and designation using nested groupingBy

        Map<String, Map<String, List<Staff>>> groupedByDeptAndDesignation = staffList.stream()

                .collect(Collectors.groupingBy(Staff::getDepartment,

                        Collectors.groupingBy(Staff::getDesignation)));

 

        // Print the result

        groupedByDeptAndDesignation.forEach((department, designationMap) -> {

            System.out.println("Department: " + department);

            designationMap.forEach((designation, staff) -> {

                System.out.println("  Designation: " + designation);

                System.out.println("    Staff: " + staff);

            });

        });

 

        // Additional aggregation: Group by department and designation with total salary

        Map<String, Map<String, Double>> salaryByDeptAndDesignation = staffList.stream()

                .collect(Collectors.groupingBy(Staff::getDepartment,

                        Collectors.groupingBy(Staff::getDesignation,

                                Collectors.summingDouble(Staff::getSalary))));

 

        // Print aggregated salary

        salaryByDeptAndDesignation.forEach((department, designationMap) -> {

            System.out.println("Department: " + department);

            designationMap.forEach((designation, totalSalary) -> {

                System.out.println("  Designation: " + designation + " - Total Salary: " + totalSalary);

            });

        });

    }

        // Generic grouping and aggregation

        Map<Object, Map<Object, Double>> salaryByDeptAndDesignations = groupAndAggregate(

                staffList,

                Staff::getDepartment, // First-level key: Department

                Staff::getDesignation, // Second-level key: Designation

                Staff::getSalary // Value to aggregate: Salary

        );

 

        // Print aggregated salary

        salaryByDeptAndDesignations.forEach((department, designationMap) -> {

            System.out.println("Department: " + department);

            designationMap.forEach((designation, totalSalary) -> {

                System.out.println("  Designation: " + designation + " - Total Salary: " + totalSalary);

            });

        });

    }

 

    // Generic method for grouping and aggregation

    public static <T, K1, K2, V> Map<K1, Map<K2, V>> groupAndAggregate(

            List<T> items,

            java.util.function.Function<T, K1> firstLevelKeyMapper,

            java.util.function.Function<T, K2> secondLevelKeyMapper,

            java.util.function.ToDoubleFunction<T> valueMapper

    ) {

        return items.stream()

                .collect(Collectors.groupingBy(firstLevelKeyMapper,

                        Collectors.groupingBy(secondLevelKeyMapper,

                                Collectors.summingDouble(valueMapper))));

    }

}

 

Output:

Grouped by Department and Designation:

Department: IT

  Designation: Developer

    Staff: [Staff{name='Alice', department='IT', designation='Developer', salary=60000.00}, Staff{name='Bob', department='IT', designation='Developer', salary=70000.00}]

  Designation: Manager

    Staff: [Staff{name='Eve', department='IT', designation='Manager', salary=90000.00}]

Department: HR

  Designation: Manager

    Staff: [Staff{name='Charlie', department='HR', designation='Manager', salary=80000.00}]

  Designation: Executive

    Staff: [Staff{name='David', department='HR', designation='Executive', salary=50000.00}]

 

Aggregated Total Salary by Department and Designation:

Output

Department: IT

  Designation: Developer - Total Salary: 130000.0

  Designation: Manager - Total Salary: 90000.0

Department: HR

  Designation: Manager - Total Salary: 80000.0

  Designation: Executive - Total Salary: 50000.0

 

Explanation:

  1. Nested Collectors.groupingBy:
    • First level: Groups by department.
    • Second level: Groups within each department by designation.
  2. Aggregation with Collectors.summingDouble:
    • Within the nested grouping, you aggregate values (e.g., salary) for each group.

 

Key Changes in the Code:

  1. Generic groupAndAggregate Method:
    • Accepts a list of items (List<T>).
    • Uses functions (Function or ToDoubleFunction) to map the attributes for keys (K1, K2) and values (V).
    • Performs grouping and aggregation using Collectors.groupingBy() and Collectors.summingDouble().
  2. Flexibility:
    • You can pass any type of object, keys, and aggregation logic.
    • It works for any List<T> where T is the type of objects you want to group.

Output:

Department: IT

  Designation: Developer - Total Salary: 130000.0

  Designation: Manager - Total Salary: 90000.0

Department: HR

  Designation: Manager - Total Salary: 80000.0

  Designation: Executive - Total Salary: 50000.0

 

 

Mastering Java 8 features
Mastering Java 8 features

 For More Java Related information, visit

Ø  Streams Lambdas Collectors and Functional Interfaces in Java 8

Ø  Java 8 support static method or default method or both in the interface

Ø  Inheritance Understand

Ø  Serialization understanding

Ø  Clone Under standing

Ø  Exception understanding

Ø  Garbage Collection Under Standing

Ø  How work Garbage Collection in Java

Ø  Under Standing Of Mutable and Immutable Class

Ø  enum understand

 

For More sort information, visit:

Ø  Selection Sort with iteration wise per step

Ø  Insertion Sort

Ø  Bubble Sort with each iteration how it is work

Ø  Merge sort of Each step how it is working

Ø  Quick Sort per iteration what happen


Post a Comment

0 Comments