Introduction
In Java applications, we often need to compare and sort objects. For primitive data types like integers or strings, Java already knows how to compare them. However, for custom objects, we need to tell Java how they should be compared and ordered.
Java provides two interfaces to help with this:
- The
Comparableinterface allows a class to define its natural ordering - The
Comparatorinterface provides external comparison logic that can be used for different sorting criteria
In this lab, you will learn how to implement both interfaces and use them to sort collections of objects in various ways.
Understanding the Comparable Interface
The Comparable interface is used when a class has a natural ordering. For example, strings are naturally ordered alphabetically, and integers are naturally ordered numerically.
When a class implements the Comparable interface, it must define a compareTo() method that specifies how instances of the class should be ordered.
Creating a Basic Student Class
Let's create a simple Student class to demonstrate how to use the Comparable interface. We'll define a student with a name, GPA, and registration number.
- Open the WebIDE and create a new file named
Student.javain the~/projectdirectory with the following code:
public class Student implements Comparable<Student> {
private String name;
private double gpa;
private int regNo;
public Student(String name, double gpa, int regNo) {
this.name = name;
this.gpa = gpa;
this.regNo = regNo;
}
// Implementing the compareTo method from Comparable interface
@Override
public int compareTo(Student other) {
// Compare students based on GPA
if (this.gpa < other.gpa) {
return -1;
} else if (this.gpa > other.gpa) {
return 1;
} else {
return 0;
}
}
// Getters
public String getName() {
return name;
}
public double getGpa() {
return gpa;
}
public int getRegNo() {
return regNo;
}
@Override
public String toString() {
return name + ", GPA: " + gpa + ", Reg No: " + regNo;
}
}
Let's understand this code:
- The class
Studentimplements theComparable<Student>interface - The
compareTo()method compares students based on their GPA - If the current student's GPA is less than the other student's GPA, we return -1
- If the current student's GPA is greater than the other student's GPA, we return 1
- If the GPAs are equal, we return 0
Now, let's create a simple test program to see how we can use the Comparable interface to sort students.
- Create a new file named
ComparableDemo.javain the~/projectdirectory with the following code:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ComparableDemo {
public static void main(String[] args) {
// Create a list of students
List<Student> students = new ArrayList<>();
students.add(new Student("John", 3.5, 101));
students.add(new Student("Mary", 3.8, 102));
students.add(new Student("Alice", 3.2, 103));
students.add(new Student("Bob", 3.9, 104));
// Print the unsorted list
System.out.println("Unsorted Student List:");
for (Student student : students) {
System.out.println(student);
}
// Sort the list using the natural ordering (defined by compareTo method)
Collections.sort(students);
// Print the sorted list
System.out.println("\nStudents sorted by GPA (using Comparable):");
for (Student student : students) {
System.out.println(student);
}
}
}
This program:
Creates a list of
StudentobjectsPrints the unsorted list
Sorts the list using
Collections.sort()which uses the natural ordering defined by thecompareTo()methodPrints the sorted list
Now, let's compile and run our code to see the output:
cd ~/project
javac Student.java ComparableDemo.java
java ComparableDemo
You should see output similar to:
Unsorted Student List:
John, GPA: 3.5, Reg No: 101
Mary, GPA: 3.8, Reg No: 102
Alice, GPA: 3.2, Reg No: 103
Bob, GPA: 3.9, Reg No: 104
Students sorted by GPA (using Comparable):
Alice, GPA: 3.2, Reg No: 103
John, GPA: 3.5, Reg No: 101
Mary, GPA: 3.8, Reg No: 102
Bob, GPA: 3.9, Reg No: 104
As you can see, the students are now sorted by their GPA in ascending order. This is the natural ordering we defined in the compareTo() method.
Creating and Using Comparators
While the Comparable interface defines a natural ordering for a class, sometimes we need to sort objects based on different criteria. This is where the Comparator interface comes in.
The Comparator interface allows us to define custom ordering logic that is separate from the class being compared. This means we can have multiple ways to sort the same class of objects.
Creating a Comparator for Name-Based Sorting
Let's create a Comparator that sorts students by their names alphabetically:
- Create a new file named
StudentNameComparator.javain the~/projectdirectory with the following code:
import java.util.Comparator;
public class StudentNameComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
// Compare students based on their names
return s1.getName().compareTo(s2.getName());
}
}
This Comparator:
- Implements the
Comparator<Student>interface - Defines a
compare()method that takes twoStudentobjects - Compares the students based on their names using the String class's own
compareTo()method
- Now, let's create a program to demonstrate how to use this
Comparator. Create a file namedComparatorDemo.javain the~/projectdirectory:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ComparatorDemo {
public static void main(String[] args) {
// Create a list of students
List<Student> students = new ArrayList<>();
students.add(new Student("John", 3.5, 101));
students.add(new Student("Mary", 3.8, 102));
students.add(new Student("Alice", 3.2, 103));
students.add(new Student("Bob", 3.9, 104));
// Print the unsorted list
System.out.println("Unsorted Student List:");
for (Student student : students) {
System.out.println(student);
}
// Sort by name using the StudentNameComparator
Collections.sort(students, new StudentNameComparator());
// Print the list sorted by name
System.out.println("\nStudents sorted by name (using Comparator):");
for (Student student : students) {
System.out.println(student);
}
}
}
This program:
Creates a list of
StudentobjectsPrints the unsorted list
Sorts the list using
Collections.sort()with our customStudentNameComparatorPrints the sorted list
Let's compile and run our code:
cd ~/project
javac Student.java StudentNameComparator.java ComparatorDemo.java
java ComparatorDemo
You should see output similar to:
Unsorted Student List:
John, GPA: 3.5, Reg No: 101
Mary, GPA: 3.8, Reg No: 102
Alice, GPA: 3.2, Reg No: 103
Bob, GPA: 3.9, Reg No: 104
Students sorted by name (using Comparator):
Alice, GPA: 3.2, Reg No: 103
Bob, GPA: 3.9, Reg No: 104
John, GPA: 3.5, Reg No: 101
Mary, GPA: 3.8, Reg No: 102
As you can see, the students are now sorted alphabetically by name, rather than by GPA.
Using Lambda Expressions for Comparators
Java 8 introduced lambda expressions, which can simplify creating comparators. Instead of creating a separate class, we can define the comparison logic inline.
- Create a new file named
LambdaComparatorDemo.javain the~/projectdirectory:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class LambdaComparatorDemo {
public static void main(String[] args) {
// Create a list of students
List<Student> students = new ArrayList<>();
students.add(new Student("John", 3.5, 101));
students.add(new Student("Mary", 3.8, 102));
students.add(new Student("Alice", 3.2, 103));
students.add(new Student("Bob", 3.9, 104));
// Sort by registration number using lambda expression
Collections.sort(students, (s1, s2) -> s1.getRegNo() - s2.getRegNo());
// Print the list sorted by registration number
System.out.println("Students sorted by registration number (using lambda):");
for (Student student : students) {
System.out.println(student);
}
// Sort by name using method reference
Collections.sort(students, Comparator.comparing(Student::getName));
// Print the list sorted by name
System.out.println("\nStudents sorted by name (using method reference):");
for (Student student : students) {
System.out.println(student);
}
}
}
This program demonstrates:
Using a lambda expression to create a comparator that sorts students by registration number
Using the
Comparator.comparing()method with a method reference to create a comparator that sorts students by nameCompile and run this code:
cd ~/project
javac Student.java LambdaComparatorDemo.java
java LambdaComparatorDemo
You should see output similar to:
Students sorted by registration number (using lambda):
John, GPA: 3.5, Reg No: 101
Mary, GPA: 3.8, Reg No: 102
Alice, GPA: 3.2, Reg No: 103
Bob, GPA: 3.9, Reg No: 104
Students sorted by name (using method reference):
Alice, GPA: 3.2, Reg No: 103
Bob, GPA: 3.9, Reg No: 104
John, GPA: 3.5, Reg No: 101
Mary, GPA: 3.8, Reg No: 102
The students are first sorted by registration number, and then by name.
Advanced Sorting Techniques
In real-world applications, we often need more complex sorting logic, such as:
- Sorting by multiple criteria (e.g., sort by GPA, and if GPAs are equal, sort by name)
- Sorting in reverse order
- Creating custom comparator chains
Let's explore these advanced techniques.
Sorting by Multiple Criteria
- Create a new file named
MultiCriteriaDemo.javain the~/projectdirectory:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class MultiCriteriaDemo {
public static void main(String[] args) {
// Create a list of students with some having the same GPA
List<Student> students = new ArrayList<>();
students.add(new Student("John", 3.5, 101));
students.add(new Student("Mary", 3.8, 102));
students.add(new Student("Alice", 3.5, 103)); // Same GPA as John
students.add(new Student("Bob", 3.8, 104)); // Same GPA as Mary
students.add(new Student("Charlie", 3.2, 105));
// Print the unsorted list
System.out.println("Unsorted Student List:");
for (Student student : students) {
System.out.println(student);
}
// Sort first by GPA, then by name
Comparator<Student> byGpa = Comparator.comparing(Student::getGpa);
Comparator<Student> byName = Comparator.comparing(Student::getName);
// Combine the comparators using thenComparing
Comparator<Student> byGpaThenName = byGpa.thenComparing(byName);
// Sort the list
Collections.sort(students, byGpaThenName);
// Print the sorted list
System.out.println("\nStudents sorted by GPA, then by name:");
for (Student student : students) {
System.out.println(student);
}
}
}
This program:
Creates a list of students, with some having the same GPA
Creates a comparator for GPA and a comparator for name
Combines these comparators using the
thenComparing()methodSorts the students first by GPA, and then by name if GPAs are equal
Compile and run the code:
cd ~/project
javac Student.java MultiCriteriaDemo.java
java MultiCriteriaDemo
You should see output similar to:
Unsorted Student List:
John, GPA: 3.5, Reg No: 101
Mary, GPA: 3.8, Reg No: 102
Alice, GPA: 3.5, Reg No: 103
Bob, GPA: 3.8, Reg No: 104
Charlie, GPA: 3.2, Reg No: 105
Students sorted by GPA, then by name:
Charlie, GPA: 3.2, Reg No: 105
Alice, GPA: 3.5, Reg No: 103
John, GPA: 3.5, Reg No: 101
Bob, GPA: 3.8, Reg No: 104
Mary, GPA: 3.8, Reg No: 102
Notice that students with the same GPA (Alice and John with 3.5, Bob and Mary with 3.8) are sorted alphabetically.
Sorting in Reverse Order
- Create a new file named
ReverseOrderDemo.javain the~/projectdirectory:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ReverseOrderDemo {
public static void main(String[] args) {
// Create a list of students
List<Student> students = new ArrayList<>();
students.add(new Student("John", 3.5, 101));
students.add(new Student("Mary", 3.8, 102));
students.add(new Student("Alice", 3.2, 103));
students.add(new Student("Bob", 3.9, 104));
students.add(new Student("Charlie", 3.0, 105));
// Print the unsorted list
System.out.println("Unsorted Student List:");
for (Student student : students) {
System.out.println(student);
}
// Method 1: Using Collections.reverseOrder() with a comparator
Comparator<Student> byGpa = Comparator.comparing(Student::getGpa);
Comparator<Student> byGpaReversed = Collections.reverseOrder(byGpa);
Collections.sort(students, byGpaReversed);
System.out.println("\nStudents sorted by GPA in descending order (Method 1):");
for (Student student : students) {
System.out.println(student);
}
// Method 2: Using the reversed() method of Comparator
Collections.sort(students, Comparator.comparing(Student::getName).reversed());
System.out.println("\nStudents sorted by name in reverse alphabetical order (Method 2):");
for (Student student : students) {
System.out.println(student);
}
}
}
This program demonstrates two ways to sort in reverse order:
Using
Collections.reverseOrder()to reverse a comparatorUsing the
reversed()method of a comparatorCompile and run the code:
cd ~/project
javac Student.java ReverseOrderDemo.java
java ReverseOrderDemo
You should see output similar to:
Unsorted Student List:
John, GPA: 3.5, Reg No: 101
Mary, GPA: 3.8, Reg No: 102
Alice, GPA: 3.2, Reg No: 103
Bob, GPA: 3.9, Reg No: 104
Charlie, GPA: 3.0, Reg No: 105
Students sorted by GPA in descending order (Method 1):
Bob, GPA: 3.9, Reg No: 104
Mary, GPA: 3.8, Reg No: 102
John, GPA: 3.5, Reg No: 101
Alice, GPA: 3.2, Reg No: 103
Charlie, GPA: 3.0, Reg No: 105
Students sorted by name in reverse alphabetical order (Method 2):
Mary, GPA: 3.8, Reg No: 102
John, GPA: 3.5, Reg No: 101
Charlie, GPA: 3.0, Reg No: 105
Bob, GPA: 3.9, Reg No: 104
Alice, GPA: 3.2, Reg No: 103
The first sorting shows students in descending order by GPA, and the second sorting shows students in reverse alphabetical order by name.
Complex Sorting Chain
- Let's create one more example that combines multiple criteria and reverse ordering. Create a file named
ComplexSortingDemo.javain the~/projectdirectory:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class ComplexSortingDemo {
public static void main(String[] args) {
// Create a list of students with varied data
List<Student> students = new ArrayList<>();
students.add(new Student("John", 3.5, 101));
students.add(new Student("Mary", 3.8, 102));
students.add(new Student("Alice", 3.5, 103)); // Same GPA as John
students.add(new Student("Bob", 3.8, 104)); // Same GPA as Mary
students.add(new Student("Charlie", 3.2, 105));
students.add(new Student("David", 3.2, 106)); // Same GPA as Charlie
// Print the unsorted list
System.out.println("Unsorted Student List:");
for (Student student : students) {
System.out.println(student);
}
// Create a complex sorting chain:
// 1. Sort by GPA in descending order
// 2. If GPAs are equal, sort by name in ascending order
// 3. If names are also equal, sort by registration number in descending order
Comparator<Student> complexComparator = Comparator
.comparing(Student::getGpa, Comparator.reverseOrder())
.thenComparing(Student::getName)
.thenComparing(Student::getRegNo, Comparator.reverseOrder());
Collections.sort(students, complexComparator);
System.out.println("\nStudents sorted by complex criteria:");
System.out.println("(GPA descending, then name ascending, then reg. number descending)");
for (Student student : students) {
System.out.println(student);
}
}
}
This program creates a complex sorting chain that:
First sorts students by GPA in descending order
If GPAs are equal, sorts by name in ascending order
If both GPA and name are equal, sorts by registration number in descending order
Compile and run the code:
cd ~/project
javac Student.java ComplexSortingDemo.java
java ComplexSortingDemo
You should see output similar to:
Unsorted Student List:
John, GPA: 3.5, Reg No: 101
Mary, GPA: 3.8, Reg No: 102
Alice, GPA: 3.5, Reg No: 103
Bob, GPA: 3.8, Reg No: 104
Charlie, GPA: 3.2, Reg No: 105
David, GPA: 3.2, Reg No: 106
Students sorted by complex criteria:
(GPA descending, then name ascending, then reg. number descending)
Bob, GPA: 3.8, Reg No: 104
Mary, GPA: 3.8, Reg No: 102
Alice, GPA: 3.5, Reg No: 103
John, GPA: 3.5, Reg No: 101
Charlie, GPA: 3.2, Reg No: 105
David, GPA: 3.2, Reg No: 106
In this output:
- Bob and Mary both have a GPA of 3.8, but Bob comes first alphabetically
- Alice and John both have a GPA of 3.5, but Alice comes first alphabetically
- Charlie and David both have a GPA of 3.2, and Charlie comes first alphabetically
This demonstrates how you can create complex, multi-level sorting logic by chaining comparators.
Building a Complete Sorting Application
Now that we've learned about the Comparable interface, the Comparator interface, and various sorting techniques, let's put everything together in a complete application.
We'll create a student management system that allows sorting students in different ways.
- First, let's create an updated version of our
Studentclass. This version will implementComparableand include an improvedtoString()method for better display. Create a file namedStudentManager.javain the~/projectdirectory:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
class Student implements Comparable<Student> {
private String name;
private double gpa;
private int regNo;
private String major;
public Student(String name, double gpa, int regNo, String major) {
this.name = name;
this.gpa = gpa;
this.regNo = regNo;
this.major = major;
}
@Override
public int compareTo(Student other) {
// Default natural ordering by registration number
return this.regNo - other.regNo;
}
// Getters
public String getName() {
return name;
}
public double getGpa() {
return gpa;
}
public int getRegNo() {
return regNo;
}
public String getMajor() {
return major;
}
@Override
public String toString() {
return String.format("%-10s | GPA: %.1f | Reg No: %-5d | Major: %-10s",
name, gpa, regNo, major);
}
}
public class StudentManager {
private List<Student> students;
public StudentManager() {
students = new ArrayList<>();
// Add some sample students
students.add(new Student("John", 3.5, 101, "Computer Science"));
students.add(new Student("Mary", 3.8, 102, "Physics"));
students.add(new Student("Alice", 3.5, 103, "Mathematics"));
students.add(new Student("Bob", 3.9, 104, "Computer Science"));
students.add(new Student("Charlie", 3.2, 105, "Physics"));
students.add(new Student("David", 3.6, 106, "Mathematics"));
students.add(new Student("Eve", 3.8, 107, "Biology"));
}
public void displayStudents() {
System.out.println("----------------------------------------------------------");
System.out.println("Name | GPA | Reg No | Major ");
System.out.println("----------------------------------------------------------");
for (Student student : students) {
System.out.println(student);
}
System.out.println("----------------------------------------------------------");
}
public void sortByNaturalOrder() {
Collections.sort(students);
System.out.println("\nStudents sorted by registration number (natural order):");
displayStudents();
}
public void sortByName() {
Collections.sort(students, Comparator.comparing(Student::getName));
System.out.println("\nStudents sorted by name:");
displayStudents();
}
public void sortByGpaDescending() {
Collections.sort(students, Comparator.comparing(Student::getGpa).reversed());
System.out.println("\nStudents sorted by GPA (descending):");
displayStudents();
}
public void sortByMajorThenGpa() {
Collections.sort(students,
Comparator.comparing(Student::getMajor)
.thenComparing(Student::getGpa, Comparator.reverseOrder()));
System.out.println("\nStudents sorted by major, then by GPA (descending):");
displayStudents();
}
public static void main(String[] args) {
StudentManager manager = new StudentManager();
Scanner scanner = new Scanner(System.in);
while (true) {
System.out.println("\nStudent Manager");
System.out.println("1. Display students (unsorted)");
System.out.println("2. Sort by registration number");
System.out.println("3. Sort by name");
System.out.println("4. Sort by GPA (highest first)");
System.out.println("5. Sort by major, then by GPA (highest first)");
System.out.println("6. Exit");
System.out.print("Enter your choice: ");
int choice = scanner.nextInt();
switch (choice) {
case 1:
System.out.println("\nUnsorted student list:");
manager.displayStudents();
break;
case 2:
manager.sortByNaturalOrder();
break;
case 3:
manager.sortByName();
break;
case 4:
manager.sortByGpaDescending();
break;
case 5:
manager.sortByMajorThenGpa();
break;
case 6:
System.out.println("Exiting program. Goodbye!");
scanner.close();
return;
default:
System.out.println("Invalid choice. Please try again.");
}
}
}
}
This application:
Defines a
Studentclass with name, GPA, registration number, and majorImplements
Comparableto define natural ordering by registration numberCreates a
StudentManagerclass that maintains a list of studentsProvides methods to sort students in different ways
Includes a simple menu-driven interface for the user to choose different sorting options
Compile and run the application:
cd ~/project
javac StudentManager.java
java StudentManager
- Test the application by trying various sorting options. Here's a sample interaction:
Student Manager
1. Display students (unsorted)
2. Sort by registration number
3. Sort by name
4. Sort by GPA (highest first)
5. Sort by major, then by GPA (highest first)
6. Exit
Enter your choice: 1
Unsorted student list:
----------------------------------------------------------
Name | GPA | Reg No | Major
----------------------------------------------------------
John | GPA: 3.5 | Reg No: 101 | Major: Computer Science
Mary | GPA: 3.8 | Reg No: 102 | Major: Physics
Alice | GPA: 3.5 | Reg No: 103 | Major: Mathematics
Bob | GPA: 3.9 | Reg No: 104 | Major: Computer Science
Charlie | GPA: 3.2 | Reg No: 105 | Major: Physics
David | GPA: 3.6 | Reg No: 106 | Major: Mathematics
Eve | GPA: 3.8 | Reg No: 107 | Major: Biology
----------------------------------------------------------
Student Manager
1. Display students (unsorted)
2. Sort by registration number
3. Sort by name
4. Sort by GPA (highest first)
5. Sort by major, then by GPA (highest first)
6. Exit
Enter your choice: 3
Students sorted by name:
----------------------------------------------------------
Name | GPA | Reg No | Major
----------------------------------------------------------
Alice | GPA: 3.5 | Reg No: 103 | Major: Mathematics
Bob | GPA: 3.9 | Reg No: 104 | Major: Computer Science
Charlie | GPA: 3.2 | Reg No: 105 | Major: Physics
David | GPA: 3.6 | Reg No: 106 | Major: Mathematics
Eve | GPA: 3.8 | Reg No: 107 | Major: Biology
John | GPA: 3.5 | Reg No: 101 | Major: Computer Science
Mary | GPA: 3.8 | Reg No: 102 | Major: Physics
----------------------------------------------------------
Student Manager
1. Display students (unsorted)
2. Sort by registration number
3. Sort by name
4. Sort by GPA (highest first)
5. Sort by major, then by GPA (highest first)
6. Exit
Enter your choice: 5
Students sorted by major, then by GPA (descending):
----------------------------------------------------------
Name | GPA | Reg No | Major
----------------------------------------------------------
Eve | GPA: 3.8 | Reg No: 107 | Major: Biology
Bob | GPA: 3.9 | Reg No: 104 | Major: Computer Science
John | GPA: 3.5 | Reg No: 101 | Major: Computer Science
David | GPA: 3.6 | Reg No: 106 | Major: Mathematics
Alice | GPA: 3.5 | Reg No: 103 | Major: Mathematics
Mary | GPA: 3.8 | Reg No: 102 | Major: Physics
Charlie | GPA: 3.2 | Reg No: 105 | Major: Physics
----------------------------------------------------------
Student Manager
1. Display students (unsorted)
2. Sort by registration number
3. Sort by name
4. Sort by GPA (highest first)
5. Sort by major, then by GPA (highest first)
6. Exit
Enter your choice: 6
Exiting program. Goodbye!
You can experiment with different sorting options to see how the list of students is displayed differently each time.
This complete application demonstrates the power and flexibility of the Comparable and Comparator interfaces in Java. It allows you to sort objects in various ways to meet different requirements of your application.
Summary
In this lab, you learned how to compare and sort objects in Java using the Comparable and Comparator interfaces.
Key concepts covered:
Comparable Interface
- Implementing
compareTo()method to define a class's natural ordering - Using
Collections.sort()with naturally ordered objects
- Implementing
Comparator Interface
- Creating custom comparators for different sorting criteria
- Using
Collections.sort()with a custom comparator - Using lambda expressions and method references for concise comparator creation
Advanced Sorting Techniques
- Sorting by multiple criteria using
thenComparing() - Sorting in reverse order using
Collections.reverseOrder()orreversed() - Creating complex sorting chains
- Sorting by multiple criteria using
Practical Application
- Building a complete student management system with various sorting options
- Implementing a user interface to demonstrate different sorting capabilities
These sorting capabilities are fundamental to many Java applications, especially those dealing with collections of data. Whether you're sorting students in a student management system, orders in an e-commerce application, or any other type of data, the techniques you learned in this lab will help you implement efficient and flexible sorting logic.



