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:
- entrySet().stream():
Converts the map's entry set into a stream of key-value pairs.
- sorted():
Sorts the stream based on a comparator. Use Map.Entry.comparingByKey() or
Map.Entry.comparingByValue() for sorting by keys or values, respectively.
- 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:
- Comparator.comparing:
Used to compare objects based on a specific attribute.
- thenComparing:
Chains multiple comparisons for additional attributes.
- Stream
API: Allows sorting with custom logic using lambdas.
- 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:
- 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.
- Flexible
Comparator:
- Use
Comparator.comparing() for the primary attribute.
- Chain
multiple sorting criteria using thenComparing().
- Use
.reversed() to sort in descending order.
- 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:
- Group
employees by department and count how many employees are in each
department.
- Group
employees by department and find the total salary in each
department.
- Group
employees by department, sort them by total salary (descending
order).
- 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
- Employee
Count by Department:
output
Employee
Count by Department: HR: 2 IT: 3 Finance: 2 |
- Total
Salary by Department:
output
Total Salary
by Department: HR: 6200.0 IT: 13200.0 Finance:
10500.0 |
- Departments
Sorted by Total Salary:
output
Departments
Sorted by Total Salary: IT: 13200.0 Finance:
10500.0 HR: 6200.0 |
- 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:
- groupingBy()
groups employees by department.
- counting()
counts employees in each department.
- summingDouble()
calculates the total salary for each department.
- Sorting:
- Departments
are sorted by total salary using sorted().
- Employees
within each department are sorted by salary using Comparator.
- 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:
- Nested
Collectors.groupingBy:
- First
level: Groups by department.
- Second
level: Groups within each department by designation.
- Aggregation
with Collectors.summingDouble:
- Within
the nested grouping, you aggregate values (e.g., salary) for each group.
Key Changes in the Code:
- 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().
- 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 |
Ø
Streams
Lambdas Collectors and Functional Interfaces in Java 8
Ø
Java
8 support static method or default method or both in the interface
Ø
Serialization
understanding
Ø
Garbage
Collection Under Standing
Ø
How
work Garbage Collection in Java
Ø
Under
Standing Of Mutable and Immutable Class
For More sort information, visit:
Ø
Selection
Sort with iteration wise per step
Ø
Bubble
Sort with each iteration how it is work
Ø
Merge
sort of Each step how it is working
Ø
Quick
Sort per iteration what happen
0 Comments