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:


About the Author:

is certified Java programmer (SCJP and SCWCD). He began programming with Java back in the days of Java 1.4 and has been passionate about it ever since. You can connect with him on Facebook and watch his Java videos on YouTube.



Attachments:
Download this file (SortingListMultipleAttributes.zip)SortingListMultipleAttributes.zip[Java source files]301 kB

Add comment

   


Comments 

#25Sriks2021-02-26 02:05
Simple way explained. Thanks a lot.
Quote
#24Raghu Prabhu2020-04-13 01:07
Hi Boss, your programming is good and easy to follow. Could you do a chapter on Junit Testing in NetBeans?
Quote
#23Raghu Prabhu2020-04-06 18:08
Simply very good. I was struggling to understand the concept. This cleared it for me.
Quote
#22Nam2020-03-16 16:19
You're right, Filip.
So to avoid potential overflow error, we should not use mathematical subtraction. Instead, we should use comparison (greater or less than).
Quote
#21Filip2020-03-11 05:16
Numeric values must not be compared using subtraction; for large values int may overflow. See:
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.
Quote