Some things—primitives—are really easy to sort. 1 comes before 2 comes before 3; z comes before q comes before c comes before a (if we’re going in reverse); and so on. But not everything we want to sort is so easy. Suppose, for example, we wanted to sort Students. Students have many properties—names, IDs, years, GPAs, majors, and so on. How do we determine who comes first? What criteria matter? Does a senior with a low GPA come before a junior with a high GPA? Does a zoology major early in the alphabet come before or after an African-American Studies major late in the alphabet?
When it comes to sorting custom objects like Students in Java, you have two
main tools at your disposal: the Comparable
interface and the Comparator
interface. Both serve to tell Java how your objects should be ordered, but
they do so in different ways and are suited to different scenarios. If your
class has a natural way of being ordered—say, students by their ID number or
last name—you can implement the Comparable interface. This means you define
a compareTo() method
inside your Student class that tells Java how to compare one Student to
another.
compareTo() returns one of three numbers—by convention, -1,
0, or 1. If x comes before y however you want to order it, then x.compareTo(y)
returns -1; if x comes after y, then the same call returns 1; if they’re equivalent,
then it returns 0.
For example, you might decide that students should be sorted
alphabetically by last name. Now, whenever you call Collections.sort() on
a list of Students, Java will use this method to determine the order. This is
great when there is a single, obvious way to sort your objects.
However, if you want to sort Students by GPA one day, by
year the next, and by major after that, the Comparator interface is the better
choice. A Comparator is a separate object that defines a compare() method,
allowing you to specify different sorting strategies without changing your
Student class. For instance, to sort by GPA, you can create a Comparator that
compares the GPA values of two students.
Comparable requires
you to implement the compareTo() method inside your class,
establishing one default sorting behavior. Comparator, on the other hand, lets
you define multiple external sorting strategies through separate objects or
lambda expressions. By understanding and applying these two interfaces, you can
efficiently sort even complex objects like Students in Java, tailoring the
sorting logic to fit your specific needs.
Let’s look at both in action. First, Comparable:
import java.util.Arrays;
class Student implements Comparable<Student> {
private String
name;
private int id;
public
Student(String name, int id) {
this.name =
name;
this.id = id;
}
public String
getName() {
return name;
}
public int getId()
{
return id;
}
@Override
public String
toString() {
return
"Student [name=" + name + ", id=" + id + "]";
}
@Override
public int
compareTo(Student other) {
if (this.id
< other.id) {
return -1;
} else if
(this.id > other.id) {
return 1;
} else {
return 0;
}
}
}
public class ComparableExample {
public static void
main(String[] args) {
Student[]
students = {
new
Student("Alice", 103),
new
Student("Bob", 101),
new
Student("Charlie", 105),
new
Student("David", 102)
};
System.out.println("Students before sorting:");
for (Student
student : students) {
System.out.println(student);
}
Arrays.sort(students);
System.out.println("\nStudents after sorting by ID:");
for (Student
student : students) {
System.out.println(student);
}
}
}
The fact that Student implements Comparable means that Students are comparable.
Notice how in main(), the ordering by IDs is not the same as the “Alice,
Bob, Charlie, David” ordering we’d get by names. The fact that we want to order
by ID, not by name, is defined in the compareTo() method, a method that must
be implemented if something is Comparable.
compareTo() rearranges the students in order by ID as we want them,
based on the results of those comparisons returning -1, 0, or 1.
Now in this other example, a whole new class—a Comparator—is invoked to make
the comparison, by way of Comparator’s compare() method. Note that this time,
the method is just “compare,” not “compareTo” as with Comparable.
import java.util.Arrays;
import java.util.Comparator;
class Student {
private String
name;
private int id;
public
Student(String name, int id) {
this.name =
name;
this.id = id;
}
public String
getName() {
return name;
}
public int getId()
{
return id;
}
@Override
public String
toString() {
return
"Student [name=" + name + ", id=" + id + "]";
}
}
public class ComparatorExample {
public static void
main(String[] args) {
Student[]
students = {
new
Student("Alice", 103),
new
Student("Bob", 101),
new
Student("Charlie", 105),
new
Student("David", 102)
};
System.out.println("Students before sorting:");
for (Student
student : students) {
System.out.println(student);
}
// Define a
Comparator to sort by name
Comparator<Student> byName = new Comparator<Student>() {
@Override
public int
compare(Student s1, Student s2) {
return
s1.getName().compareTo(s2.getName());
}
};
Arrays.sort(students, byName);
System.out.println("Students after sorting by name:");
for (Student
student : students) {
System.out.println(student);
}
}
}
Here, the comparison strategy changes. Comparing strings is very easy to do
with compareTo. (Strings are naturally Comparable.) Feeding two strings into compareTo()
as we have done here will put them in A-Z alphabetical order. (As an exercise,
think about how you could reverse this to Z-A order.) Therefore, the Comparator,
calling Comparable, puts the Students in order by name, as opposed to ID in the
previous example.
If you have an array that isn’t easily sorted (as in our case of the array of Students,
since Students have more than one property), Java allows you, in
Arrays.sort, to pass in a Comparator to tell sort on what criteria to perform
the sort.
No comments:
Post a Comment