Blog Java Java 8 Features

Exploring Java’s Collectors Class: A Comprehensive Guide

In Java 8, the introduction of the Stream API brought a new functional programming style to the language. One of the most useful classes in this API is Collectors, which provides a set of convenient reduction operations, allowing you to perform complex data operations in a concise and readable manner.

In this blog post, we’ll explore the Collectors class and its various methods, complete with examples to help you understand and leverage its power.

Setting Up the Example

Before we dive into the methods, let’s define a simple Person class that we’ll use for our examples:

class Person {
    private String name;
    private int age;
    private double salary;

    // Constructor, getters, and setters omitted for brevity
}

Now, let’s create a list of Person objects that we’ll use throughout the examples:

List<Person> people = Arrays.asList(
    new Person("Alice", 25, 50000),
    new Person("Bob", 30, 60000),
    new Person("Charlie", 35, 70000),
    new Person("David", 40, 80000),
    new Person("Eve", 45, 90000),
    new Person("Alice", 27, 55000)
);

Exploring Collectors Methods

1. toList()

The toList() method collects the stream elements into a new List instance. Here’s an example that creates a list of names from the people list:

List<String> names = people.stream()
                            .map(Person::getName)
                            .collect(Collectors.toList());
// names = [Alice, Bob, Charlie, David, Eve, Alice]

2. toSet()

The toSet() method collects the stream elements into a new Set instance, eliminating duplicates. Let’s use it to get a set of unique names:

Set<String> uniqueNames = people.stream()
                                .map(Person::getName)
                                .collect(Collectors.toSet());
// uniqueNames = [Alice, Bob, Charlie, David, Eve]

3. toMap()

The toMap() method collects the stream elements into a Map instance, where you can specify how to derive the keys and values. Here’s an example that creates a map of names to salaries:

Map<String, Double> namesToSalaries = people.stream()
                                            .collect(Collectors.toMap(
                                                Person::getName,
                                                Person::getSalary
                                            ));
// namesToSalaries = {Alice=55000.0, Bob=60000.0, Charlie=70000.0, David=80000.0, Eve=90000.0}

4. groupingBy()

The groupingBy() method groups the stream elements by a specified function. Let’s group people by their age:

Map<Integer, List<Person>> peopleByAge = people.stream()
                                               .collect(Collectors.groupingBy(Person::getAge));
/*
peopleByAge = {
    25=[Alice(25, 50000.0)],
    30=[Bob(30, 60000.0)],
    35=[Charlie(35, 70000.0)],
    40=[David(40, 80000.0)],
    45=[Eve(45, 90000.0)],
    27=[Alice(27, 55000.0)]
}
*/

5. joining()

The joining() method concatenates the string representations of the stream elements, optionally separated by a delimiter. Here’s an example that creates a comma-separated list of names:

String namesCommaSeparated = people.stream()
                                    .map(Person::getName)
                                    .collect(Collectors.joining(", "));
// namesCommaSeparated = "Alice, Bob, Charlie, David, Eve, Alice"

6. counting()

The counting() method counts the number of elements in the stream. Let’s count the number of people in our list:

long peopleCount = people.stream()
                         .collect(Collectors.counting());
// peopleCount = 6

7. averagingInt(), averagingLong(), averagingDouble()

These methods calculate the average of the stream elements after applying the provided mapping function. Here’s an example that calculates the average age of people:

double averageAge = people.stream()
                          .collect(Collectors.averagingInt(Person::getAge));
// averageAge = 35.0

8. summarizingInt(), summarizingLong(), summarizingDouble()

These methods produce a SummaryStatistics object that captures various summary data about the elements in the stream, such as count, sum, average, min, and max. Let’s calculate some summary statistics for salaries:

DoubleSummaryStatistics salarySummary = people.stream()
                                              .collect(Collectors.summarizingDouble(Person::getSalary));
/*
salarySummary = {
    count=6,
    sum=350000.0,
    min=50000.0,
    average=58333.333333333336,
    max=90000.0
}
*/

These are just a few examples of the powerful methods available in the Collectors class. There are many more methods that can help you perform various data operations efficiently and concisely.

Conclusion

The Collectors class in Java’s Stream API provides a rich set of utility methods for performing common data processing tasks. By understanding and leveraging these methods, you can write more concise and expressive code, making your codebase more readable and maintainable.

In this blog post, we covered several essential methods from the Collectors class, including toList(), toSet(), toMap(), groupingBy(), joining(), counting(), averagingInt(), averagingLong(), averagingDouble(), summarizingInt(), summarizingLong(), and summarizingDouble(). However, the Collectors class offers many more methods to explore, so be sure to consult the official Java documentation for a comprehensive understanding of its capabilities.

Happy coding!

Avatar

Neelabh

About Author

As Neelabh Singh, I am a Senior Software Engineer with 6.6 years of experience, specializing in Java technologies, Microservices, AWS, Algorithms, and Data Structures. I am also a technology blogger and an active participant in several online coding communities.

You may also like

Blog Design Pattern

Understanding the Builder Design Pattern in Java | Creational Design Patterns | CodeTechSummit

Overview The Builder design pattern is a creational pattern used to construct a complex object step by step. It separates
Blog Tech Toolkit

Base64 Decode

Base64 encoding is a technique used to encode binary data into ASCII characters, making it easier to transmit data over