Say we are working on a class representing a Person by their first and last names. We have created a basic class to do this and implemented proper equals and hashCode methods.
public class Person { private final String lastName; //invariant - nonnull private final String firstName; //invariant - nonnull public Person(String firstName, String lastName){ this.firstName = firstName != null ? firstName : ""; this.lastName = lastName != null ? lastName : ""; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public String toString() { return lastName + ", " + firstName; } @Override public boolean equals(Object o) { if (! (o instanceof Person)) return false; Person p = (Person)o; return firstName.equals(p.firstName) && lastName.equals(p.lastName); } @Override public int hashCode() { return Objects.hash(firstName, lastName); } }
Now we would like to sort a list of Person objects by their name, such as in the following scenario:
public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("John", "Doe"), new Person("Bob", "Dole"), new Person("Ronald", "McDonald"), new Person("Alice", "McDonald"), new Person("Jill", "Doe") ); Collections.sort(people); //This currently won't work. }
Unfortunately, as marked, the above currently won't compile. Collections.sort(..) only knows how to sort a list if the elements in that list are comparable, or a custom method of comparison is given.
If you were asked to sort the following list : 1,3,5,4,2, you'd have no problem saying the answer is 1,2,3,4,5. This is because Integers (both in Java and mathematically) have a natural ordering, a standard, default comparison base ordering. To give our Person class a natural ordering, we implement Comparable
public class Person implements Comparable<Person> { private final String lastName; //invariant - nonnull private final String firstName; //invariant - nonnull public Person(String firstName, String lastName) { this.firstName = firstName != null ? firstName : ""; this.lastName = lastName != null ? lastName : ""; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public String toString() { return lastName + ", " + firstName; } @Override public boolean equals(Object o) { if (! (o instanceof Person)) return false; Person p = (Person)o; return firstName.equals(p.firstName) && lastName.equals(p.lastName); } @Override public int hashCode() { return Objects.hash(firstName, lastName); } @Override public int compareTo(Person other) { // If this' lastName and other's lastName are not comparably equivalent, // Compare this to other by comparing their last names. // Otherwise, compare this to other by comparing their first names int lastNameCompare = lastName.compareTo(other.lastName); if (lastNameCompare != 0) { return lastNameCompare; } else { return firstName.compareTo(other.firstName); } } }
Now, the main method given will function correctly
public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("John", "Doe"), new Person("Bob", "Dole"), new Person("Ronald", "McDonald"), new Person("Alice", "McDonald"), new Person("Jill", "Doe") ); Collections.sort(people); //Now functions correctly //people is now sorted by last name, then first name: // --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald }
If, however, you either do not want or are unable to modify class Person, you can provide a custom Comparator<T> that handles the comparison of any two Person objects. If you were asked to sort the following list: circle, square, rectangle, triangle, hexagon you could not, but if you were asked to sort that list based on the number of corners, you could. Just so, providing a comparator instructs Java how to compare two normally not comparable objects.
public class PersonComparator implements Comparator<Person> { public int compare(Person p1, Person p2) { // If p1's lastName and p2's lastName are not comparably equivalent, // Compare p1 to p2 by comparing their last names. // Otherwise, compare p1 to p2 by comparing their first names if (p1.getLastName().compareTo(p2.getLastName()) != 0) { return p1.getLastName().compareTo(p2.getLastName()); } else { return p1.getFirstName().compareTo(p2.getFirstName()); } } } //Assume the first version of Person (that does not implement Comparable) is used here public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("John", "Doe"), new Person("Bob", "Dole"), new Person("Ronald", "McDonald"), new Person("Alice", "McDonald"), new Person("Jill", "Doe") ); Collections.sort(people); //Illegal, Person doesn't implement Comparable. Collections.sort(people, new PersonComparator()); //Legal //people is now sorted by last name, then first name: // --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald }
Comparators can also be created/used as an anonymous inner class
//Assume the first version of Person (that does not implement Comparable) is used here public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("John", "Doe"), new Person("Bob", "Dole"), new Person("Ronald", "McDonald"), new Person("Alice", "McDonald"), new Person("Jill", "Doe") ); Collections.sort(people); //Illegal, Person doesn't implement Comparable. Collections.sort(people, new PersonComparator()); //Legal //people is now sorted by last name, then first name: // --> Jill Doe, John Doe, Bob Dole, Alice McDonald, Ronald McDonald //Anonymous Class Collections.sort(people, new Comparator<Person>() { //Legal public int compare(Person p1, Person p2) { //Method code... } }); } Version ≥ Java SE 8
Lambda expression based comparators
As of Java 8, comparators can also be expressed as lambda expressions
//Lambda Collections.sort(people, (p1, p2) -> { //Legal //Method code.... });
Comparator default methods
Furthermore, there are interesting default methods on the Comparator interface for building comparators : the following builds a comparator comparing by lastName and then firstName.
Collections.sort(people, Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName));
Inversing the order of a comparator
Any comparator can also easily be reversed using the reversedMethod which will change ascending order to descending.
Learn All in Tamil © Designed & Developed By Tutor Joes | Privacy Policy | Terms & Conditions