Introduction
Java 8 introduced the Stream API, which enables developers to perform complex data processing operations in a more declarative and readable way. One of the most powerful features of the Stream API is the ability to group elements in a collection using Collectors.groupingBy(). Grouping allows you to organize data into subgroups based on a classifier function, making it easier to analyze or manipulate the data.
In this guide, we will explore how to group elements in a stream using Collectors.groupingBy(). We’ll look at different scenarios, including simple grouping by a single field and more complex groupings with multiple levels.
Table of Contents
- Problem Statement
- Solution Steps
- Java Program
- Basic Grouping by a Single Field
- Grouping by Multiple Fields
- Grouping and Counting Elements
- Advanced Considerations
- Conclusion
Problem Statement
The task is to create a Java program that:
- Groups elements in a collection based on a specific criterion.
- Uses the
Collectors.groupingBy()method to perform the grouping. - Outputs the grouped elements.
Example 1:
- Input: List of
Productobjects, where each product has acategoryandprice. - Output: Map of products grouped by
category.
Example 2:
- Input: List of
Productobjects, grouped by bothcategoryandprice range. - Output: Nested map of products grouped by
categoryand then byprice range.
Solution Steps
- Create a Stream: Start with a stream of elements that you want to group.
- Group the Elements Using
Collectors.groupingBy(): Apply the grouping function based on the desired criterion. - Display the Result: Print the grouped elements or process them further.
Java Program
Basic Grouping by a Single Field
A common use case for Collectors.groupingBy() is grouping objects by a single field. For example, you might want to group products by their category.
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Java 8 - Basic Grouping by a Single Field
* Author: https://www.rameshfadatare.com/
*/
public class GroupingBySingleField {
public static void main(String[] args) {
// Step 1: Create a list of products
List<Product> products = List.of(
new Product("Laptop", "Electronics", 1500),
new Product("Phone", "Electronics", 800),
new Product("Tablet", "Electronics", 600),
new Product("Sofa", "Furniture", 1200),
new Product("Chair", "Furniture", 300)
);
// Step 2: Group products by category
Map<String, List<Product>> productsByCategory = products.stream()
.collect(Collectors.groupingBy(Product::getCategory));
// Step 3: Display the result
productsByCategory.forEach((category, productList) -> {
System.out.println("Category: " + category);
productList.forEach(product ->
System.out.println(" Product: " + product.getName() + ", Price: " + product.getPrice()));
});
}
}
// Custom class Product
class Product {
private String name;
private String category;
private double price;
public Product(String name, String category, double price) {
this.name = name;
this.category = category;
this.price = price;
}
public String getName() {
return name;
}
public String getCategory() {
return category;
}
public double getPrice() {
return price;
}
}
Output
Category: Electronics
Product: Laptop, Price: 1500.0
Product: Phone, Price: 800.0
Product: Tablet, Price: 600.0
Category: Furniture
Product: Sofa, Price: 1200.0
Product: Chair, Price: 300.0
Explanation
- The
products.stream()method creates a stream ofProductobjects. - The
collect(Collectors.groupingBy(Product::getCategory))method groups the products by theircategory. - The grouped products are stored in a map where the key is the category and the value is the list of products in that category.
Grouping by Multiple Fields
You can also group elements by multiple fields, such as grouping by category and then by price range.
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Java 8 - Grouping by Multiple Fields
* Author: https://www.rameshfadatare.com/
*/
public class GroupingByMultipleFields {
public static void main(String[] args) {
// Step 1: Create a list of products
List<Product> products = List.of(
new Product("Laptop", "Electronics", 1500),
new Product("Phone", "Electronics", 800),
new Product("Tablet", "Electronics", 600),
new Product("Sofa", "Furniture", 1200),
new Product("Chair", "Furniture", 300)
);
// Step 2: Group products by category and then by price range
Map<String, Map<String, List<Product>>> productsByCategoryAndPrice = products.stream()
.collect(Collectors.groupingBy(
Product::getCategory,
Collectors.groupingBy(product -> {
if (product.getPrice() < 500) return "Budget";
else if (product.getPrice() <= 1000) return "Mid-Range";
else return "Premium";
})
));
// Step 3: Display the result
productsByCategoryAndPrice.forEach((category, priceMap) -> {
System.out.println("Category: " + category);
priceMap.forEach((priceRange, productList) -> {
System.out.println(" Price Range: " + priceRange);
productList.forEach(product ->
System.out.println(" Product: " + product.getName() + ", Price: " + product.getPrice()));
});
});
}
}
Output
Category: Electronics
Price Range: Premium
Product: Laptop, Price: 1500.0
Price Range: Mid-Range
Product: Phone, Price: 800.0
Product: Tablet, Price: 600.0
Category: Furniture
Price Range: Premium
Product: Sofa, Price: 1200.0
Price Range: Budget
Product: Chair, Price: 300.0
Explanation
- The
groupingBy(Product::getCategory, Collectors.groupingBy(...))method groups the products first by category and then by price range. - The nested map structure allows you to organize products by multiple criteria, making it easier to analyze the data.
Grouping and Counting Elements
In some cases, you may want to group elements and then count how many elements fall into each group.
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Java 8 - Grouping and Counting Elements
* Author: https://www.rameshfadatare.com/
*/
public class GroupingAndCounting {
public static void main(String[] args) {
// Step 1: Create a list of products
List<Product> products = List.of(
new Product("Laptop", "Electronics", 1500),
new Product("Phone", "Electronics", 800),
new Product("Tablet", "Electronics", 600),
new Product("Sofa", "Furniture", 1200),
new Product("Chair", "Furniture", 300)
);
// Step 2: Group products by category and count them
Map<String, Long> productCountByCategory = products.stream()
.collect(Collectors.groupingBy(Product::getCategory, Collectors.counting()));
// Step 3: Display the result
productCountByCategory.forEach((category, count) ->
System.out.println("Category: " + category + ", Count: " + count));
}
}
Output
Category: Electronics, Count: 3
Category: Furniture, Count: 2
Explanation
- The
collect(Collectors.groupingBy(Product::getCategory, Collectors.counting()))method groups the products by category and counts the number of products in each category. - The result is a map where the key is the category and the value is the count of products in that category.
Advanced Considerations
-
Custom Collectors: You can create custom collectors to perform more complex operations during grouping, such as calculating averages or summing values.
-
Immutable Collections: If you need the resulting maps to be immutable, consider using
Collectors.toUnmodifiableMap()or wrapping the map withCollections.unmodifiableMap(). -
Performance Considerations: Grouping operations can be resource-intensive, especially with large datasets. Ensure your application’s performance requirements are met, and consider using parallel streams if appropriate.
Conclusion
This guide provides methods for grouping elements in Java 8 using the Collectors.groupingBy() method, covering basic grouping, grouping by multiple fields, and grouping with counting. Grouping elements is a powerful way to organize and analyze data, making it easier to perform operations based on specific criteria. By mastering the use of Collectors.groupingBy(), you can handle complex data processing tasks more effectively in your Java applications.