๐ฅ Java 8 Stream APIโโโA Complete Guide (Part-2)
Advanced Java 8 Stream APIโโโgroupingBy, partitioningBy, flatMap, Custom Collectors & More
๐ฅ Java 8 Stream API โ A Complete Guide (Part-2)
Advanced Java 8 Stream API โ
groupingBy,partitioningBy,flatMap, Custom Collectors & More

Stream API Advanced
๐ Part-1: Java 8 Stream API โ A Complete Guide
In Part-1, we explored the fundamentals of Java 8 Stream API like map, filter, and reduce.
In this part, weโll dive deeper into powerful operations that help solve real-world problems, including:
- Grouping and partitioning collections
- Flattening nested data
- Creating custom collectors
- Multi-level aggregations
Collectors.groupingBy()
- A classifier function that maps an element to a key.
- (Optional) Downstream collector (e.g., counting, summing, mapping, etc.)
- It returns a
Map<K, List<T>>(or anotherMap<K, R>if using downstream collectors).
Collectors.groupingBy(Function<? super T, ? extends K>)
Collectors.groupingBy(Function, Collector)
โ Problem Statement:
You need to group employees by their department to populate a department-wise team view.
public class Employee {
String name;
String department;
Employee(String name, String department) {
this.name = name;
this.department = department;
}
public String getDepartment() { return department; }
public String getName() { return name; }
}
List<Employee> employees = Arrays.asList(
new Employee("Alice", "HR"),
new Employee("Bob", "Engineering"),
new Employee("Charlie", "HR"),
new Employee("David", "Engineering")
);
Map<String, List<Employee>> groupedByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment));
groupedByDept.forEach((dept, empList) -> {
String names = empList.stream()
.map(Employee::getName)
.collect(Collectors.joining(", "));
System.out.println(dept + ": " + names);
});
โ Output:
HR: Alice, Charlie
Engineering: Bob, David
Collectors.partitioningBy()
- A Predicate to test each element (returns true/false).
- A
Map<Boolean, List<T>>where: trueโ elements matching the predicatefalseโ the rest
Collectors.partitioningBy(Predicate<? super T>)
โ Problem Statement:
You need to separate even and odd numbers from a list of integers.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
Map<Boolean, List<Integer>> partitioned = numbers.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
System.out.println("Even numbers: " + partitioned.get(true));
System.out.println("Odd numbers: " + partitioned.get(false));
โ Output:
Even numbers: [2, 4, 6]
Odd numbers: [1, 3, 5]
flatMap() โ Flattening Nested Structures
- A function that returns a
Streamfor each element. - A flattened stream of all sub-elements.
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)
โ Problem Statement:
You have a nested list of programming languages, and you want a flat list of all languages.
List<List<String>> nestedList = Arrays.asList(
Arrays.asList("Java", "Python"),
Arrays.asList("C++", "Go"),
Arrays.asList("Rust")
);
List<String> flatList = nestedList.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(flatList);
โ Output:
[Java, Python, C++, Go, Rust]
Custom Collectors
Collector.of(Supplier, Accumulator, Combiner, Finisher)
What it takes:
- Supplier โ provides new container
- Accumulator โ adds elements to container
- Combiner โ merges containers (parallel streams)
- Finisher โ final transformation
- A custom aggregation result โ e.g., formatted string, calculated value, etc.
โ Problem Statement:
You want to join a list of strings in uppercase, separated by " | ".
List<String> words = Arrays.asList("java", "stream", "collector");
Collector<String, StringJoiner, String> customCollector = Collector.of(
() -> new StringJoiner(" | "),
(joiner, word) -> joiner.add(word.toUpperCase()),
StringJoiner::merge,
StringJoiner::toString
);
String result = words.stream().collect(customCollector);
System.out.println(result);
โ Output:
JAVA | STREAM | COLLECTOR
Real-World Example: Group & Count
Method: groupingBy + counting
Count how many elements are grouped under each key.
Collectors.groupingBy(..., Collectors.counting())
Returns:
A Map<K, Long>
โ Problem Statement:
You want to count how many books each author has written.
class Book {
String title;
String author;
Book(String title, String author) {
this.title = title;
this.author = author;
}
public String getAuthor() { return author; }
}
List<Book> books = Arrays.asList(
new Book("Book A", "Author 1"),
new Book("Book B", "Author 2"),
new Book("Book C", "Author 1"),
new Book("Book D", "Author 3"),
new Book("Book E", "Author 2")
);
Map<String, Long> bookCountByAuthor = books.stream()
.collect(Collectors.groupingBy(Book::getAuthor, Collectors.counting()));
bookCountByAuthor.forEach((author, count) ->
System.out.println(author + ": " + count));
โ Output:
Author 1: 2
Author 2: 2
Author 3: 1
Nested Grouping
Collectors.groupingBy(Classifier1, groupingBy(Classifier2))
What it does:
Groups elements in multiple levels, like by department โ then by designation โ then by list of employees.
โ Problem Statement
You need to generate multi-level reports, like employees grouped by department and then by designation.
class Employee {
String name;
String department;
String designation;
Employee(String name, String department, String designation) {
this.name = name;
this.department = department;
this.designation = designation;
}
public String getDepartment() { return department; }
public String getDesignation() { return designation; }
public String getName() { return name; }
}
List<Employee> employees = Arrays.asList(
new Employee("Alice", "HR", "Manager"),
new Employee("Bob", "Engineering", "Developer"),
new Employee("Charlie", "HR", "Recruiter"),
new Employee("David", "Engineering", "Developer"),
new Employee("Eva", "Engineering", "Manager")
);
Map<String, Map<String, List<Employee>>> multiLevelGroup = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.groupingBy(Employee::getDesignation)));
multiLevelGroup.forEach((dept, desMap) -> {
System.out.println("Department: " + dept);
desMap.forEach((designation, empList) -> {
String names = empList.stream()
.map(Employee::getName)
.collect(Collectors.joining(", "));
System.out.println(" " + designation + ": " + names);
});
});
โ Output:
Department: HR
Manager: Alice
Recruiter: Charlie
Department: Engineering
Developer: Bob, David
Manager: Eva
โ ๏ธ Tips for Using Advanced Stream API
- โ
flatMap()is powerful for flattening but can make code harder to read โ use wisely. - โ
groupingBy()andpartitioningBy()are great for aggregation โ but watch out for memory usage in large datasets. - โ
Use
Collectors.toMap()when you want to directly create aMap<K, V>. - โ Prefer custom collectors when built-in ones donโt solve the problem exactly.
โ Conclusion
Java 8โs Stream API is a true game-changer โ with its advanced operations, you can handle complex grouping, data flattening, partitioning, and custom aggregations, all in a clean, declarative, and readable way. Whether youโre processing logs, generating reports, or working with nested data, mastering these techniques will help you write modern, efficient, and maintainable Java code.
Happy coding! ๐ปโจ
๋ฉํ๋ฐ์ดํฐ
- post_id
- 3b8f6d43fcc8
- slug
- java-8-stream-api-a-complete-guide-part-2-3b8f6d43fcc8
- url
- https://medium.com/javarevisited/java-8-stream-api-a-complete-guide-part-2-3b8f6d43fcc8
- canonical_url
- https://medium.com/javarevisited/java-8-stream-api-a-complete-guide-part-2-3b8f6d43fcc8
- author_url
- https://medium.com/@singhprince0088
- status
- ok
- fetched_at
- 2026-07-17 00:53:25