Tools
Tools: A Complete Guide to Collectors in Java 8 Streams - Part 2
2026-03-04
0 views
admin
Downstream Collectors (Advanced) ## How It Works ## collectingAndThen() ## Creating a Custom Collector ## How It Works ## Parallel Streams & Collectors ## Performance Considerations ## When to Use collect() vs reduce() ## Conclusion ## What's next? In the last part we saw, Now we will continue and take a dive into Collectors can be chained. Example: Group by department and calculate average salary. Assuming we have the following employee list groupingBy(Employee::getDepartment) → Groups employees by department. mapping(Employee::getName, toList()) → Instead of collecting full Employee objects, it extracts only the name. Result type: Map<String, List<String>> Applies finishing transformation. Example: Make result immutable. Sometimes built-in collectors are not enough. You can create one using: Example: Collect into a StringBuilder. Creates a new StringBuilder Appends each element to the StringBuilder: Used only in parallel streams to merge partial results.
In sequential streams, it’s effectively not needed. Converts StringBuilder to String. If you use parallel streams: The combiner becomes critical. Avoid shared mutable state outside collector. groupingByConcurrent() is better for parallel streams Avoid unnecessary boxing (mapToInt() when possible) Prefer primitive collectors (summingInt) over reduce() ✔ Prefer built-in collectors
✔ Use downstream collectors effectively
✔ Handle duplicate keys in toMap()
✔ Avoid side effects
✔ Use collectingAndThen() for immutability
✔ Use primitive streams when possible reduce() → For immutable reduction collect() → For mutable accumulation (most real-world cases) Collectors are not just about converting streams into lists. Mastering Collectors means mastering the real power of Java 8 Streams. This concludes collectors in depth, next we will see Advanced Stream Techniques. Templates let you quickly answer FAQs or store snippets for re-use. Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment's permalink. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse COMMAND_BLOCK:
Map<String, Double> avgSalary = employees.stream() .collect(Collectors.groupingBy( Employee::getDepartment, Collectors.averagingInt(Employee::getSalary) )); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
Map<String, Double> avgSalary = employees.stream() .collect(Collectors.groupingBy( Employee::getDepartment, Collectors.averagingInt(Employee::getSalary) )); COMMAND_BLOCK:
Map<String, Double> avgSalary = employees.stream() .collect(Collectors.groupingBy( Employee::getDepartment, Collectors.averagingInt(Employee::getSalary) )); COMMAND_BLOCK:
List<Employee> employees = Arrays.asList( new Employee("Amit", "IT", "Developer", 60000), new Employee("Neha", "IT", "Tester", 50000), new Employee("Raj", "HR", "Recruiter", 40000), new Employee("Simran", "HR", "Manager", 70000), new Employee("Karan", "Sales", "Executive", 45000)
); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
List<Employee> employees = Arrays.asList( new Employee("Amit", "IT", "Developer", 60000), new Employee("Neha", "IT", "Tester", 50000), new Employee("Raj", "HR", "Recruiter", 40000), new Employee("Simran", "HR", "Manager", 70000), new Employee("Karan", "Sales", "Executive", 45000)
); COMMAND_BLOCK:
List<Employee> employees = Arrays.asList( new Employee("Amit", "IT", "Developer", 60000), new Employee("Neha", "IT", "Tester", 50000), new Employee("Raj", "HR", "Recruiter", 40000), new Employee("Simran", "HR", "Manager", 70000), new Employee("Karan", "Sales", "Executive", 45000)
); CODE_BLOCK:
{ IT=55000.0, HR=55000.0, Sales=45000.0
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{ IT=55000.0, HR=55000.0, Sales=45000.0
} CODE_BLOCK:
{ IT=55000.0, HR=55000.0, Sales=45000.0
} COMMAND_BLOCK:
Map<String, List<String>> namesByDept = employees.stream() .collect(Collectors.groupingBy( Employee::getDepartment, Collectors.mapping( Employee::getName, Collectors.toList() ) )); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
Map<String, List<String>> namesByDept = employees.stream() .collect(Collectors.groupingBy( Employee::getDepartment, Collectors.mapping( Employee::getName, Collectors.toList() ) )); COMMAND_BLOCK:
Map<String, List<String>> namesByDept = employees.stream() .collect(Collectors.groupingBy( Employee::getDepartment, Collectors.mapping( Employee::getName, Collectors.toList() ) )); CODE_BLOCK:
{ IT=[Amit, Neha], HR=[Raj, Simran], Sales=[Karan]
} Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
{ IT=[Amit, Neha], HR=[Raj, Simran], Sales=[Karan]
} CODE_BLOCK:
{ IT=[Amit, Neha], HR=[Raj, Simran], Sales=[Karan]
} COMMAND_BLOCK:
List<String> names = employees.stream() .map(Employee::getName) .collect(Collectors.collectingAndThen( Collectors.toList(), Collections::unmodifiableList )); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
List<String> names = employees.stream() .map(Employee::getName) .collect(Collectors.collectingAndThen( Collectors.toList(), Collections::unmodifiableList )); COMMAND_BLOCK:
List<String> names = employees.stream() .map(Employee::getName) .collect(Collectors.collectingAndThen( Collectors.toList(), Collections::unmodifiableList )); CODE_BLOCK:
[Amit, Neha, Raj, Simran, Karan] Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
[Amit, Neha, Raj, Simran, Karan] CODE_BLOCK:
[Amit, Neha, Raj, Simran, Karan] CODE_BLOCK:
Collector.of( supplier, accumulator, combiner, finisher
); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
Collector.of( supplier, accumulator, combiner, finisher
); CODE_BLOCK:
Collector.of( supplier, accumulator, combiner, finisher
); COMMAND_BLOCK:
Collector<String, StringBuilder, String> customCollector = Collector.of( StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString ); Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
Collector<String, StringBuilder, String> customCollector = Collector.of( StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString ); COMMAND_BLOCK:
Collector<String, StringBuilder, String> customCollector = Collector.of( StringBuilder::new, StringBuilder::append, StringBuilder::append, StringBuilder::toString ); CODE_BLOCK:
String result = Stream.of("A", "B", "C") .collect(customCollector); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
String result = Stream.of("A", "B", "C") .collect(customCollector); CODE_BLOCK:
String result = Stream.of("A", "B", "C") .collect(customCollector); CODE_BLOCK:
ABC Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
stream.parallel().collect(...) Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
stream.parallel().collect(...) CODE_BLOCK:
stream.parallel().collect(...) COMMAND_BLOCK:
List<String> list = new ArrayList<>();
stream.forEach(list::add); // Not thread-safe Enter fullscreen mode Exit fullscreen mode COMMAND_BLOCK:
List<String> list = new ArrayList<>();
stream.forEach(list::add); // Not thread-safe COMMAND_BLOCK:
List<String> list = new ArrayList<>();
stream.forEach(list::add); // Not thread-safe CODE_BLOCK:
stream.parallel().collect(Collectors.toList()); Enter fullscreen mode Exit fullscreen mode CODE_BLOCK:
stream.parallel().collect(Collectors.toList()); CODE_BLOCK:
stream.parallel().collect(Collectors.toList()); - What is a Collector?
- How collect() Works Internally
- Commonly Used Built-in Collectors
- Grouping and Partitioning - Downstream Collectors (Advanced)
- collectingAndThen()
- Creating a custom collector
- Parallel streams and collectors - IT → (60000 + 50000) / 2 = 55000.0
- HR → (40000 + 70000) / 2 = 55000.0
- Sales → (45000) / 1 = 45000.0 - Append "A" → "A"
- Append "B" → "AB"
- Append "C" → "ABC" - Associative
- Non-interfering - Build custom reduction logic
how-totutorialguidedev.toai