Core java - Advance Topics
  • Welcome
  • Schedule
  • 1) Exception Handling
    • 1) Introduction to Exception Handling
    • 2) Categories of Exceptions
    • 3) Creating a method that throws an exception
    • 4) Creating Custom Exception Classes
    • 5)What happens when an exception is thrown?
      • 5.1) Creating try-catch-finally blocks
      • 5.2) Using a method that throws a checked exception
      • 5.3) Using a method that throws a runtime exception
      • 5.4) Using a method that throws an error
      • 5.5) Will a finally block execute even if the catch block defines a return statement?
      • 5.6) What happens if both a catch and a finally block define return statement?
      • 5.7) What happens if a finally block modifies the value returned from a catch block?
      • 5.8) Can a try block be followed only by a finally block?
      • 5.9) Does the order of the exceptions caught in the catch blocks matter?
      • 5.10) Can I rethrow an exception or the error I catch?
      • 5.11) Can I declare my methods to throw a checked exception instead of handling it?
      • 5.12) I can create nested loops, so can I create nested try-catch blocks too?
      • 5.13) Should I handle errors?
    • 6) Best Practices
    • 7) Cheat Sheet
    • 8) Problems
  • 2) Wrapper Classes and Enums
    • 2.1) Creating objects of the wrapper classes
    • Enums
  • 3) Inner Classes
    • 3.1) Static nested class (also called static inner class)
    • 3.2) Inner class (also called member class)
    • 3.3) Anonymous inner class
    • 3.4) Method local inner classes
    • CheatSheet
  • 4) Generics
    • Multiple Type parameters in Generic classes
    • Inheritance using Generics
    • Generic interfaces
    • Generic Methods
    • Bounded type parameters
    • Applications
  • 5) Equals and Hashcode
    • Problems
  • CompareTo method overview
  • Basic DS
    • 1) Simple Array List
    • 2) Simple HashMap
  • 5) Collections Framework - Part 1
    • Introducing the collections framework
    • Working with the Collection interface
      • The core Collection interface
      • Methods of the Collection interface
    • Creating and using List, Set, and Deque implementations
      • List interface and its implementations
      • Iterators
      • Sorting List using custom sorting technique
      • Comparable Interface
      • Custom Sorting using comparator
      • ArrayList - Examples and practice problems
    • Stack
    • Linked List
    • LinkedList Operations
  • 6) Collections Framework - Part 2
    • Sets
      • Set Types
      • Array to Set (vice versa)
    • Maps
    • TreeMap
    • Autoboxing And Unboxing
  • Collections Framework - Part 3
    • Basics : DS , Number System
    • Internal Working
      • HashMap
      • HashSet
  • 7) Reflection API
  • 8) Annotations
  • 9) Reading Input From Various Sources
    • File Handling
    • Reading From Xml
    • Reading From JSON
  • 10) Multi-threading (Concurrency)
    • Protect shared data
    • Thread-safe access to shared data
  • 11) Design Patterns
    • Singleton
    • DI
  • 12) Internal Working of JVM
  • 13) Garbage Collection
  • 14) More on Strings (Buffer and Builder)
  • 15) Cloning and Immutable Class
    • 16) Serialization And Deserialization
    • Untitled
  • JAVA 8
    • Interface Changes
    • Lambda
    • Method Ref
    • Optional
    • Streams
    • Predicates
  • Practice Tests
    • Test - Collections
    • OOPS
    • S-OOPS
Powered by GitBook
On this page
  • Java Comparator interface intuition
  • Sort collection by more than one field:
  • Examples : (with Java 8)
  • 1)
  • 2) Using Java 8 Comparator default methods
  • Example :

Was this helpful?

  1. 5) Collections Framework - Part 1
  2. Creating and using List, Set, and Deque implementations

Custom Sorting using comparator

PreviousComparable InterfaceNextArrayList - Examples and practice problems

Last updated 6 years ago

Was this helpful?

Java Comparator interface intuition

The Comparable interface that we looked at in the previous section defines a default ordering for the objects of a class. This default ordering is also called the natural ordering of the objects.

But what if you need to alter the default ordering just for a single requirement? For example, what if you want to sort the Employee objects in the previous example based on their names, not IDs?

You can’t change the implementation of the compareTo() function because it will affect the ordering everywhere, not just for your particular requirement.

Also, If you’re dealing with a predefined Java class or a class defined in a third party library, you won’t be able to change the default ordering. For example, The default ordering of String objects is to order them alphabetically. But what if you want to order them based on their length?

For such cases, Java provides a Comparator interface. You can define a Comparator and pass it to the sorting functions like or to sort the objects based on the ordering defined by the Comparator.

The Comparator interface contains a method called compare() that you need to implement in order to define the ordering of the objects of a class -

public interface Comparator<T> {
    int compare(T o1, T o2);
}

The implementation of the compare() method should return

  • a negative integer, if the first argument is less than the second,

  • zero, if the first argument is equal to the second, and

  • a positive integer, if the first argument is greater than the second.

Let’s see an example to make things clear.

How does Collections.Sort() work? Internally the Sort method does call Compare method of the classes it is sorting. To compare two elements, it asks “Which is greater?” Compare method returns -1, 0 or 1 to say if it is less than, equal, or greater to the other. It uses this result to then determine if they should be swapped for its sort.

Working Program:

// Java program to demonstrate working of Comparator 
// interface 
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

// A class to represent a student. 
class Student {
	int rollno;
	String name, address;

	// Constructor
	public Student(int rollno, String name, String address) {
		this.rollno = rollno;
		this.name = name;
		this.address = address;
	}

	// Used to print student details in main()
	public String toString() {
		return this.rollno + " " + this.name + " " + this.address;
	}
}

class Sortbyroll implements Comparator<Student> {
	// Used for sorting in ascending order of
	// roll number
	public int compare(Student a, Student b) {
		return a.rollno - b.rollno;
	}
}

class Sortbyname implements Comparator<Student> {
	// Used for sorting in ascending order of
	// roll name
	public int compare(Student a, Student b) {
		return a.name.compareTo(b.name);
	}
}

// Driver class
public class Main {
	public static void main(String[] args) {
		ArrayList<Student> ar = new ArrayList<Student>();
		ar.add(new Student(111, "bbbb", "london"));
		ar.add(new Student(131, "aaaa", "nyc"));
		ar.add(new Student(121, "cccc", "jaipur"));

		System.out.println("Unsorted");
		for (int i = 0; i < ar.size(); i++)
			System.out.println(ar.get(i));

		Collections.sort(ar, new Sortbyroll());

		System.out.println("\nSorted by rollno");
		for (int i = 0; i < ar.size(); i++)
			System.out.println(ar.get(i));

		Collections.sort(ar, new Sortbyname());

		System.out.println("\nSorted by name");
		for (int i = 0; i < ar.size(); i++)
			System.out.println(ar.get(i));
	}
}
Unsorted
111 bbbb london
131 aaaa nyc
121 cccc jaipur

Sorted by rollno
111 bbbb london
121 cccc jaipur
131 aaaa nyc

Sorted by name
131 aaaa nyc
111 bbbb london
121 cccc jaipur

Sort collection by more than one field:

In previous articles, we have discussed how to sort list of objects on the basis of single field using Comparable and Comparator interface But, what if we have a requirement to sort ArrayList objects in accordance with more than one fields like firstly sort, according to student name and secondly sort according to student roll number.

Below is the implementation of above approach:


// Java program to demonstrate working of Comparator 
// interface 
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

// A class to represent a student. 
class Student {
	Integer rollno;
	String name, address;

	public Integer getRollno() {
		return rollno;
	}

	public void setRollno(Integer rollno) {
		this.rollno = rollno;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public Student(Integer rollno, String name, String address) {
		super();
		this.rollno = rollno;
		this.name = name;
		this.address = address;
	}

	// Used to print student details in main()
	public String toString() {
		return this.rollno + " " + this.name + " " + this.address;
	}
}

class SortByNameAndRollNum implements Comparator<Student> {
	// Used for sorting in ascending order of
	// roll name
	public int compare(Student a, Student b) {

		// for comparison
		int NameCompare = a.getName().compareTo(b.getName());
		int RollNumCompare = a.getRollno().compareTo(b.getRollno());

		// 2-level comparison using if-else block
		if (NameCompare == 0) {
			return ((RollNumCompare == 0) ? NameCompare : RollNumCompare);
		} else {
			return NameCompare;
		}

	}
}

// Driver class
public class Main {
	public static void main(String[] args) {
		ArrayList<Student> ar = new ArrayList<Student>();
		ar.add(new Student(111, "aaaa", "london"));
		ar.add(new Student(131, "aaaa", "nyc"));
		ar.add(new Student(121, "cccc", "jaipur"));

		System.out.println("Unsorted");
		for (int i = 0; i < ar.size(); i++)
			System.out.println(ar.get(i));

		Collections.sort(ar, new SortByNameAndRollNum());

		System.out.println("\nSorted by namd and rolNum");
		for (int i = 0; i < ar.size(); i++)
			System.out.println(ar.get(i));

	}
}
Unsorted
111 aaaa london
131 aaaa nyc
121 cccc jaipur

Sorted by namd and rolNum
111 aaaa london
131 aaaa nyc
121 cccc jaipur

Examples : (with Java 8)

1)

import java.time.LocalDate;

class Employee  {
    private int id;
    private String name;
    private double salary;
    private LocalDate joiningDate;

    public Employee(int id, String name, double salary, LocalDate joiningDate) {
        this.id = id;
        this.name = name;
        this.salary = salary;
        this.joiningDate = joiningDate;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public LocalDate getJoiningDate() {
        return joiningDate;
    }

    public void setJoiningDate(LocalDate joiningDate) {
        this.joiningDate = joiningDate;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", salary=" + salary +
                ", joiningDate=" + joiningDate +
                '}';
    }
}
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ComparatorExample {
    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();

        employees.add(new Employee(1010, "Rajeev", 100000.00, LocalDate.of(2010, 7, 10)));
        employees.add(new Employee(1004, "Chris", 95000.50, LocalDate.of(2017, 3, 19)));
        employees.add(new Employee(1015, "David", 134000.00, LocalDate.of(2017, 9, 28)));
        employees.add(new Employee(1009, "Steve", 100000.00, LocalDate.of(2016, 5, 18)));

        System.out.println("Employees : " + employees);

        // Sort employees by Name
        Comparator<Employee> employeeNameComparator = new Comparator<Employee>() {
            @Override
            public int compare(Employee e1, Employee e2) {
                return e1.getName().compareTo(e2.getName());
            }
        };

        /*
        The above Comparator can also be written using lambda expression like so =>
        employeeNameComparator = (e1, e2) -> e1.getName().compareTo(e2.getName());

        Which can be shortened even further using Java 8 Comparator default method
        employeeNameComparator = Comparator.comparing(Employee::getName)
        */

        Collections.sort(employees, employeeNameComparator);
        System.out.println("\nEmployees (Sorted by Name) : " + employees);

        // Sort employees by Salary
        Comparator<Employee> employeeSalaryComparator = new Comparator<Employee>() {
            @Override
            public int compare(Employee e1, Employee e2) {
                if(e1.getSalary() < e2.getSalary()) {
                    return -1;
                } else if (e1.getSalary() > e2.getSalary()) {
                    return 1;
                } else {
                    return 0;
                }
            }
        };
        
        Collections.sort(employees, employeeSalaryComparator);
        System.out.println("\nEmployees (Sorted by Salary) : " + employees);

        // Sort employees by JoiningDate
        Comparator<Employee> employeeJoiningDateComparator = new Comparator<Employee>() {
            @Override
            public int compare(Employee e1, Employee e2) {
                return e1.getJoiningDate().compareTo(e2.getJoiningDate());
            }
        };

        Collections.sort(employees, employeeJoiningDateComparator);
        System.out.println("\nEmployees (Sorted by JoiningDate) : " + employees);
    }
}
Employees : [Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}]

Employees (Sorted by Name) : [Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}, Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}]

Employees (Sorted by Salary) : [Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}]

Employees (Sorted by JoiningDate) : [Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}, Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}]

As you saw in the above example, All the sorting methods also accept an instance of a Comparatorinterface. They use the ordering defined by the Comparator interface’s compare() function to sort the objects.

2) Using Java 8 Comparator default methods

The Comparator interface contains various default factory methods for creating Comparator instances.

All the Comparators that we created in the previous section can be made more concise by using these factory methods.

Here is the same Comparator example that we saw in the previous section using Java 8 Comparator default methods -

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class ComparatorExample {
    public static void main(String[] args) {
        List<Employee> employees = new ArrayList<>();

        employees.add(new Employee(1010, "Rajeev", 100000.00, LocalDate.of(2010, 7, 10)));
        employees.add(new Employee(1004, "Chris", 95000.50, LocalDate.of(2017, 3, 19)));
        employees.add(new Employee(1015, "David", 134000.00, LocalDate.of(2017, 9, 28)));
        employees.add(new Employee(1009, "Steve", 100000.00, LocalDate.of(2016, 5, 18)));

        System.out.println("Employees : " + employees);

        // Sort employees by Name
        Collections.sort(employees, Comparator.comparing(Employee::getName));
        System.out.println("\nEmployees (Sorted by Name) : " + employees);

        // Sort employees by Salary
        Collections.sort(employees, Comparator.comparingDouble(Employee::getSalary));
        System.out.println("\nEmployees (Sorted by Salary) : " + employees);

        // Sort employees by JoiningDate
        Collections.sort(employees, Comparator.comparing(Employee::getJoiningDate));
        System.out.println("\nEmployees (Sorted by JoiningDate) : " + employees);

        // Sort employees by Name in descending order
        Collections.sort(employees, Comparator.comparing(Employee::getName).reversed());
        System.out.println("\nEmployees (Sorted by Name in descending order) : " + employees);

        // Chaining multiple Comparators
        // Sort by Salary. If Salary is same then sort by Name
        Collections.sort(employees, Comparator.comparingDouble(Employee::getSalary).thenComparing(Employee::getName));
        System.out.println("\nEmployees (Sorted by Salary and Name) : " + employees);
    }
}
# Output

Employees : [Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}]
Employees (Sorted by Name) : [Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}, Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}]
Employees (Sorted by Salary) : [Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}]
Employees (Sorted by JoiningDate) : [Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}, Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}]
Employees (Sorted by Name in descending order) : [Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}, Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}, Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}]
Employees (Sorted by Salary and Name) : [Employee{id=1004, name='Chris', salary=95000.5, joiningDate=2017-03-19}, Employee{id=1010, name='Rajeev', salary=100000.0, joiningDate=2010-07-10}, Employee{id=1009, name='Steve', salary=100000.0, joiningDate=2016-05-18}, Employee{id=1015, name='David', salary=134000.0, joiningDate=2017-09-28}]

Example :

package certification.collections;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

class Student {

	private int id;
	private String name;
	private int age;
	private double fees;

	public Student(int id, String name, int age, double fees) {
		super();
		this.id = id;
		this.name = name;
		this.age = age;
		this.fees = fees;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public double getFees() {
		return fees;
	}

	public void setFees(double fees) {
		this.fees = fees;
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", age=" + age + ", fees=" + fees + "]";
	}

}

class FeesComparator implements Comparator<Student> {

	@Override
	public int compare(Student o1, Student o2) {

		return (int) (o2.getFees() - o1.getFees());
	}

}

class IdComparator implements Comparator<Student> {

	@Override
	public int compare(Student o1, Student o2) {
		// TODO Auto-generated method stub
		return o1.getId() - o2.getId();
	}

}

public class TestComparator {

	public static void main(String[] args) {

		// creating test data
		Student student;
		List<Student> list = new ArrayList<Student>(5);
		for (int i = 1; i < 6; i++) {
			student = new Student(i, "Mohit" + i, i + 20, 24.7 + i);
			list.add(student);
		}

		System.out.println(list);

		Collections.sort(list, new FeesComparator());

		System.out.println(list);

		Collections.sort(list, new IdComparator());

		System.out.println(list);

		Collections.sort(list, new Comparator<Student>() {

			@Override
			public int compare(Student o1, Student o2) {
				// TODO Auto-generated method stub
				return o1.getName().compareTo(o2.getName());
			}
		});

		System.out.println(list);
	}

}
Collections.sort
Arrays.sort