DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
  1. DZone
  2. Coding
  3. Java
  4. How to Use Java Stream Collectors

How to Use Java Stream Collectors

Want to learn more about using Java Stream collectors? Check out this post on collectors and how to use them.

Yogen Rai user avatar by
Yogen Rai
CORE ·
Jul. 20, 18 · Tutorial
Like (36)
Save
Tweet
Share
38.06K Views

Join the DZone community and get the full member experience.

Join For Free

Java 8 has introduced a new abstraction called stream, letting us process data in a declarative way. Furthermore, streams can leverage multi-core architectures without you having to write a single line of multithread code.

Collectors are a class an implementations of Collector that implement various useful reduction operations, such as accumulating elements into collections, summarizing elements according to various criteria, etc.

Using Collectors

To demonstrate the usage of stream  Collectors, let me define a class to hold my data as:

class Employee {
    private String empId;
    private String name;
    private Double salary;
    private String department;

    public Employee(String empId, String name, Double salary, String department) {
        this.empId = empId;
        this.name = name;
        this.salary = salary;
        this.department = department;
    }

    // getters and toString
}


So, let me create a list of  Employeeas:

Employee john = new Employee("E123", "John Nhoj", 200.99, "IT");
Employee south = new Employee("E223", "South Htuos", 299.99, "Sales");
Employee reet = new Employee("E133", "Reet Teer", 300.99, "IT");
Employee prateema = new Employee("E143", "Prateema Rai", 300.99, "Benefits");
Employee yogen = new Employee("E323", "Yogen Rai", 200.99, "Sales");

List<Employee> employees = Arrays.asList(john, south, reet, prateema, yogen);


Calculating Statistical Values

Finding Average Salary

Double averageSalary = employees.stream().collect(averagingDouble(Employee::getSalary));
// 260.79


Similarly, there are averagingInt(ToIntFunction<? super T> mapper)and averagingLong(ToLongFunction<? super T> mapper) to find the average values for  Integer  and  Long  types.

Finding Total Salary

Double totalSalary = employees.stream().collect(summingDouble(Employee::getSalary));
// 1303.95


summingInt(ToIntFunction<? super T> mapper) and summingLong(ToLongFunction<? super T> mapper) are available for summing Integer and Long types.

Finding Max Salary

Double maxSalary = employees.stream().collect(collectingAndThen(maxBy(comparingDouble(Employee::getSalary)), emp -> emp.get().getSalary()));
// 300.99


 collectingAndThen  function has declaration of:

Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher)


 Function finisher  can be used to format the final result of the  Collector output as:

String avgSalary = employees.stream()
        .collect(collectingAndThen(averagingDouble(Employee::getSalary), new DecimalFormat("'$'0.000")::format));
// $260.790


Calculating Statistics in One Shot

DoubleSummaryStatistics statistics = employees.stream().collect(summarizingDouble(Employee::getSalary));
System.out.println("Average: " + statistics.getAverage() + ", Total: " + statistics.getSum() + ", Max: " + statistics.getMax() + ", Min: "+ statistics.getMin());
// Average: 260.79, Total: 1303.95, Max: 300.99, Min: 200.99                                                             


Similarly, summarizingInt(ToIntFunction<? super T> mapper)and summarizingLong(ToLongFunction<? super T> mapper) are available for Integer  and Long  types.

Mapping and Joining Stream

Mapping Only Employee Names

List<String> employeeNames = employees.stream().collect(mapping(Employee::getName, toList()));
// [John Nhoj, South Htuos, Reet Teer, Prateema Rai, Yogen Rai]


Joining Employee Names

String employeeNamesStr = employees.stream().map(Employee::getName).collect(joining(","));
// John Nhoj,South Htuos,Reet Teer,Prateema Rai,Yogen Rai


The joining() function has overloaded version to take prefix as suffix as:

Collector<CharSequence,?,String> joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)


So, if you want to collect employee names in a specific format, then, you can do:

String employeeNamesStr = employees.stream().map(Employee::getName).collect(joining(", ", "Employees = {", "}"));
// Employees = {John Nhoj, South Htuos, Reet Teer, Prateema Rai, Yogen Rai}


Grouping Elements

Grouping employees by Department

 groupingBy()  takes classifier  Function  as:

Collector<T,?,Map<K,List<T>>> groupingBy(Function<? super T,? extends K> classifier)


So, grouping of employees by department is:

Map<String, List<Employee>> deptEmps = employees.stream().collect(groupingBy(Employee::getDepartment)); 

// {Sales=[{empId='E223', name='South Htuos', salary=299.99, department='Sales'}, {empId='E323', name='Yogen Rai', salary=200.99, department='Sales'}], Benefits=[{empId='E143', name='Prateema Rai', salary=300.99, department='Benefits'}], IT=[{empId='E123', name='John Nhoj', salary=200.99, department='IT'}, {empId='E133', name='Reet Teer', salary=300.99, department='IT'}]}


Counting Employees per Department

There is an overloaded version of  groupingBy() as:

Collector<T,?,Map<K,List<T>>> groupingBy(Function<? super T,? extends K> classifier,Collector<? super T,A,D> downstream)


So, counting employees per department would be:

Map<String, Long> deptEmpsCount = employees.stream().collect(groupingBy(Employee::getDepartment, counting()));
// {Sales=2, Benefits=1, IT=2}


Calculating Average Salary per Department With Sorted Department Name

Another overload method of  groupingBy()is:

Collector<T,?,M> groupingBy(Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream)


 TreeMap can be used to  groupBy department name sorted as:

Map<String, Double> averageSalaryDeptSorted = employees.stream().collect(groupingBy(Employee::getDepartment, TreeMap::new, averagingDouble(Employee::getSalary)));
// {Benefits=300.99, IT=250.99, Sales=250.49}


There is a  ConcurrentHashMap version of groupBy(),  leveraging multi-core architectures.

Map<String, Long> deptEmpCount = employees.stream().collect(groupingByConcurrent(Employee::getDepartment, counting())); 
// {Sales=2, IT=2, Benefits=1}


Partitioning Elements

 partitioningBy()takes a predicate to partion the result into true for meeting the predicate criterion and false for not as:

Collector<T,?,Map<Boolean,List<T>>> partitioningBy(Predicate<? super T> predicate)


Finding employees with a salary greater than the average salary is:

Map<Boolean, List<Employee>> portionedEmployees = employees.stream().collect(partitioningBy(e -> e.getSalary() > averageSalary));
// {false=[{empId='E123', name='John Nhoj', salary=200.99, department='IT'}, {empId='E323', name='Yogen Rai', salary=200.99, department='Sales'}], 
true=[{empId='E223', name='South Htuos', salary=299.99, department='Sales'}, {empId='E133', name='Reet Teer', salary=300.99, department='IT'}, {empId='E143', name='Prateema Rai', salary=300.99, department='Benefits'}]}


You can use overloaded version of this method to filter the result as:

Collector<T,?,Map<Boolean,D>> partitioningBy(Predicate<? super T> predicate, Collector<? super T,A,D> downstream)


Conclusion

The Collectors class has many utility functions to operate over the stream and extract the result meaningfully.

All the source code for the example above are available on GitHub.

Stream (computing) Java (programming language)

Published at DZone with permission of Yogen Rai, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • How To Set Up and Run Cypress Test Cases in CI/CD TeamCity
  • Microservices 101: Transactional Outbox and Inbox
  • mTLS Everywere
  • Microservices Testing

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: