Java Sort a List by multiple attributes example
- Details
- Written by Nam Ha Minh
- Last Updated on 17 June 2019   |   Print Email
There are several ways in order to sort a list collection by multiple attributes (keys) of its elements type. For example, sorting a list of employees by their job title, then by age, and then by salary. In this article, we are going to discuss the two common ways which are easy to understand and implement. That are, using a chained comparator, and using a CompareToBuilderclass of the Apache Commons Lang library.
1. The model class
Suppose that we have to manage a list of employees whose type is declared by the following class:
package net.codejava.collections; public class Employee { private String name; private String jobTitle; private int age; private int salary; public Employee(String name, String jobTitle, int age, int salary) { this.name = name; this.jobTitle = jobTitle; this.age = age; this.salary = salary; } // getters and setters public String toString() { return String.format("%s\t%s\t%d\t%d", name, jobTitle, age, salary); } }
Note that the Employee class overrides the toString() method in order to provide a meaningful information of an Employee object when printing the object using the System.out.println() method.
2. Using a chained Comparator
Create the EmployeeChainedComparator class looks like as follows:
package net.codejava.collections; import java.util.Arrays; import java.util.Comparator; import java.util.List; /** * This is a chained comparator that is used to sort a list by multiple * attributes by chaining a sequence of comparators of individual fields * together. * @author www.codejava.net * */ public class EmployeeChainedComparator implements Comparator<Employee> { private List<Comparator<Employee>> listComparators; @SafeVarargs public EmployeeChainedComparator(Comparator<Employee>... comparators) { this.listComparators = Arrays.asList(comparators); } @Override public int compare(Employee emp1, Employee emp2) { for (Comparator<Employee> comparator : listComparators) { int result = comparator.compare(emp1, emp2); if (result != 0) { return result; } } return 0; } }
The key points here are this comparator takes a list of comparators passed via its constructor; and the compare() method iterates over this comparators list to compare two Employee objects by each individual comparator. Now, we create separate comparators for each field by which we want to sort: job title, age and salary.
Job title comparator:
package net.codejava.collections; import java.util.Comparator; /** * This comparator compares two employees by their job titles. * @author www.codejava.net * */ public class EmployeeJobTitleComparator implements Comparator<Employee> { @Override public int compare(Employee emp1, Employee emp2) { return emp1.getJobTitle().compareTo(emp2.getJobTitle()); } }
Age comparator:
package net.codejava.collections; import java.util.Comparator; /** * This comparator compares two employees by their ages. * @author www.codejava.net * */ public class EmployeeAgeComparator implements Comparator<Employee> { @Override public int compare(Employee emp1, Employee emp2) { return emp1.getAge() - emp2.getAge(); } }
Salary comparator:
package net.codejava.collections; import java.util.Comparator; /** * This comparator compares two employees by their salaries. * @author www.codejava.net * */ public class EmployeeSalaryComparator implements Comparator<Employee> { @Override public int compare(Employee emp1, Employee emp2) { return emp1.getSalary() - emp2.getSalary(); } }
Now, write a test program with some dummy data as follows:
package net.codejava.collections; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * This program demonstrates how to sort a list collection by multiple * attributes using a chained comparator. * * @author www.codejava.net * */ public class SortingMultipleAttributesExample { public static void main(String[] args) { System.out.println("===== SORTING BY MULTIPLE ATTRIBUTES ====="); List<Employee> listEmployees = new ArrayList<Employee>(); listEmployees.add(new Employee("Tom", "Developer", 45, 80000)); listEmployees.add(new Employee("Sam", "Designer", 30, 75000)); listEmployees.add(new Employee("Bob", "Designer", 45, 134000)); listEmployees.add(new Employee("Peter", "Programmer", 25, 60000)); listEmployees.add(new Employee("Tim", "Designer", 45, 130000)); listEmployees.add(new Employee("Craig", "Programmer", 30, 52000)); listEmployees.add(new Employee("Anne", "Programmer", 25, 51000)); listEmployees.add(new Employee("Alex", "Designer", 30, 120000)); listEmployees.add(new Employee("Bill", "Programmer", 22, 30000)); listEmployees.add(new Employee("Samuel", "Developer", 28, 80000)); listEmployees.add(new Employee("John", "Developer", 35, 67000)); listEmployees.add(new Employee("Patrick", "Developer", 35, 140000)); listEmployees.add(new Employee("Alice", "Programmer", 35, 80000)); listEmployees.add(new Employee("David", "Developer", 35, 99000)); listEmployees.add(new Employee("Jane", "Designer", 30, 82000)); System.out.println("*** Before sorting:"); for (Employee emp : listEmployees) { System.out.println(emp); } Collections.sort(listEmployees, new EmployeeChainedComparator( new EmployeeJobTitleComparator(), new EmployeeAgeComparator(), new EmployeeSalaryComparator()) ); System.out.println("\n*** After sorting:"); for (Employee emp : listEmployees) { System.out.println(emp); } } }
Output:
===== SORTING BY MULTIPLE ATTRIBUTES ===== *** Before sorting: Tom Developer 45 80000 Sam Designer 30 75000 Bob Designer 45 134000 Peter Programmer 25 60000 Tim Designer 45 130000 Craig Programmer 30 52000 Anne Programmer 25 51000 Alex Designer 30 120000 Bill Programmer 22 30000 Samuel Developer 28 80000 John Developer 35 67000 Patrick Developer 35 140000 Alice Programmer 35 80000 David Developer 35 99000 Jane Designer 30 82000 *** After sorting: Sam Designer 30 75000 Jane Designer 30 82000 Alex Designer 30 120000 Tim Designer 45 130000 Bob Designer 45 134000 Samuel Developer 28 80000 John Developer 35 67000 David Developer 35 99000 Patrick Developer 35 140000 Tom Developer 45 80000 Bill Programmer 22 30000 Anne Programmer 25 51000 Peter Programmer 25 60000 Craig Programmer 30 52000 Alice Programmer 35 80000
As we can see, the list of employees is sorted firstly by job title, secondly by age, and finally salary, in ascending order.
3. Using a CompareToBuilder
The Apache Commons Lang API provides the CompareToBuilder class that can be used to sort a list collection by multiple attributes in a much simpler way. Just write a comparator as simple as follows:
package net.codejava.collections; import java.util.Comparator; import org.apache.commons.lang3.builder.CompareToBuilder; /** * This comparator sorts a list of Employees by job title, age and salary * into ascending order. * @author www.codejava.net * */ public class EmployeeComparator implements Comparator<Employee> { @Override public int compare(Employee o1, Employee o2) { return new CompareToBuilder() .append(o1.getJobTitle(), o2.getJobTitle()) .append(o1.getAge(), o2.getAge()) .append(o1.getSalary(), o2.getSalary()).toComparison(); } }
And sort the list using this comparator:
Collections.sort(listEmployees, new EmployeeComparator());
Output is as same as using the chained comparator above.
And note that you have to put the commons-lang-VERSION.jar file to the classpath.
4. Switching sort order: ascending to descending
To reverse sort order from ascending to descending of a particular attribute, simply swap the order of the two objects being compared in the comparator. For example, the following comparator sorts the list by job title and age in ascending order, but by salary in descending order:
public class EmployeeComparator implements Comparator<Employee> { @Override public int compare(Employee o1, Employee o2) { return new CompareToBuilder() .append(o1.getJobTitle(), o2.getJobTitle()) .append(o1.getAge(), o2.getAge()) .append(o2.getSalary(), o1.getSalary()).toComparison(); } }
When running the above test program with this comparator, the output will be:
Before sorting: Tom Developer 45 80000 Sam Designer 30 75000 Bob Designer 45 134000 Peter Programmer 25 60000 Tim Designer 45 130000 Craig Programmer 30 52000 Anne Programmer 25 51000 Alex Designer 30 120000 Bill Programmer 22 30000 Samuel Developer 28 80000 John Developer 35 67000 Patrick Developer 35 140000 Alice Programmer 35 80000 David Developer 35 99000 Jane Designer 30 82000 After sorting: Alex Designer 30 120000 Jane Designer 30 82000 Sam Designer 30 75000 Bob Designer 45 134000 Tim Designer 45 130000 Samuel Developer 28 80000 Patrick Developer 35 140000 David Developer 35 99000 John Developer 35 67000 Tom Developer 45 80000 Bill Programmer 22 30000 Peter Programmer 25 60000 Anne Programmer 25 51000 Craig Programmer 30 52000 Alice Programmer 35 80000
Related Sorting tutorials:
Other Java Collections Tutorials:
- Java Set Tutorial
- Java Map Tutorial
- Java List Tutorial and
- Java Queue Tutorial
- Understanding equals() and hashCode() in Java
- Understanding Object Ordering in Java with Comparable and Comparator
- 18 Java Collections and Generics Best Practices
Comments
So to avoid potential overflow error, we should not use mathematical subtraction. Instead, we should use comparison (greater or less than).
final int a1 = Integer.MAX_VALUE;
final int a2 = Integer.MIN_VALUE;
System.out.println(a1 - a2);
System.out.println(Integer.compare(a1, a2));
Int.compare(int, int) or corresponding Long.compare(long, long) must be used.