Objects Comparison in Java. Comparable, Comparator


Topics interesting only for Java programmers

Link to this posting

Postby Ursego » 05 Mar 2019, 07:58

Copy the following fragment to a text file with .java extension.

To enjoy keywords coloring, open this file in Eclipse. After that, enable word-wrap by pressing Alt + Shift + Y (otherwise you will need to scroll right to read long comments). Set the code editor width so the very first line of the file (with asterisks) fits one line (i.e. is not broken into 2 lines).

You can also open this file in Notepad++. It will understand by the file extension (.java), that it's Java code, and paint keywords. After that, enable word-wrap by marking the menu option View > Word wrap.

In any editor, use the Fixedsys font (as I did) to see straight vertical alignment.

Code: Select all
// java.lang.Comparable interface:

// Comparable is an interface defining a strategy of comparing an object with other objects of the same type. This is called the class's "natural ordering". The Comparable interface has only one method:
public interface Comparable<T> {
   public int compareTo(T other);
}
// The implementation of compareTo() must return:
//   * The number zero is returned when the current object is equal to the argument to compareTo(). The equals() must return true in this case, and only in this case.
//   * A number less than zero is returned when the current object is smaller than the argument to compareTo().
//   * A number greater than zero is returned when the current object is larger than the argument to compareTo().
public class Player implements Comparable<Player> {
    @Override
    public int compareTo(Player otherPlayer) {
        return (this.getRanking() - otherPlayer.getRanking());
    }
}

// java.util.Comparator interface:

// Sometimes you want to sort an object that did not implement Comparable, or you want to sort objects in different ways at different times (while only one version of Comparable.compareTo can be implemented). The Comparator interface defines a compare(arg1, arg2) method with two arguments which represent compared objects and works similarly to the Comparable.compareTo() method:
public interface Comparator<T> {
   int   compare(T o1, T o2) // Returns a negative integer, zero, or a positive integer as the first argument is less than, equal to, or greater than the second
}
// In our first example, we'll create a Comparator to use the ranking attribute of Player to sort the players:
public class PlayerRankingComparator implements Comparator<Player> {
    @Override
    public int compare(Player firstPlayer, Player secondPlayer) {
       return (firstPlayer.getRanking() - secondPlayer.getRanking());
    }
}
// Similarly, we can create a Comparator to use the age attribute of Player to sort the players:
public class PlayerAgeComparator implements Comparator<Player> {
    @Override
    public int compare(Player firstPlayer, Player secondPlayer) {
       return (firstPlayer.getAge() - secondPlayer.getAge());
    }
}
// The Comparable interface is a good choice when used for defining the default ordering or, in other words, if it's the main way of comparing objects. Then, we must ask ourselves why use a Comparator if we already have Comparable? There are several reasons why:
//   * Sometimes, we can't modify the source code of the class whose objects we want to sort, thus making the use of Comparable impossible
//   * Using Comparators allows us to avoid adding additional code to our domain classes
//   * We can define multiple different comparison strategies which isn't possible when using Comparable

// Using Lambdas with Comparator:
public class Duck implements Comparable<Duck> {
   private String name;
   private int weight;
   public Duck(String name, int weight) { this.name = name; this.weight = weight; }
   public String getName() { return name; }
   public int getWeight() { return weight; }
   public String toString() { return name; }
   public int compareTo(Duck d) { return name.compareTo(d.name); }
}
// The Duck class itself can define compareTo() in only one way. In this case, name was chosen. If we want to sort by something else, we have to define that sort order outside the compareTo() method:
public static void main(String[] args) {
   Comparator<Duck> byWeight = new Comparator<Duck>() {
      public int compare(Duck d1, Duck d2) { return d1.getWeight()-d2.getWeight(); }
   };
   List<Duck> ducks = new ArrayList<>();
   ducks.add(new Duck("Quack", 7));
   ducks.add(new Duck("Puddles", 10));
   Collections.sort(ducks);
   System.out.println(ducks); // [Puddles, Quack]
   Collections.sort(ducks, byWeight);
   System.out.println(ducks); // [Quack, Puddles]
}
// First, we defined an inner class with the comparator. Then we sorted without the comparator and with the comparator to see the difference in output. Comparator is a functional interface since there is only one abstract method to implement. This means that we can rewrite the comparator in the previous example as any of the following:
Comparator<Duck> byWeight = (d1, d2) -> d1.getWeight() - d2.getWeight();
Comparator<Duck> byWeight = (Duck d1, Duck d2) -> d1.getWeight() - d2.getWeight();
Comparator<Duck> byWeight = (d1, d2) -> { return d1.getWeight() - d2.getWeight(); };
Comparator<Duck> byWeight = (Duck d1, Duck d2) -> {return d1.getWeight() - d2.getWeight(); };

// Both Comparator and Comparable are functional interfaces. However, using a lambda for Comparable would be silly. The point of Comparable is to implement it inside the object being compared.

// ####### Searching and Sorting

// The Collections.sort() method uses the compareTo() method to sort. It expects the objects to be sorted to be Comparable.
public class SortRabbits {
   static class Rabbit { int id; }
   public static void main(String[] args) {
      List<Rabbit> rabbits = new ArrayList<>();
      rabbits.add(new Rabbit());
      Collections.sort(rabbits); // DOES NOT COMPILE
   }
}
// Java knows that the Rabbit class is not Comparable. It knows sorting will fail, so it doesn't even let the code compile.

// THE SOLUTION:
// You can fix this by passing a Comparator to sort(). Remember that a Comparator is useful when you want to specify sort order without using a compareTo() method. The methods sort() and binarySearch() allow you to pass in a Comparator object as the 2nd argument when you don't want to use the natural order:
import java.util.*;
public class SortRabbits {
   static class Rabbit{ int id; }
   public static void main(String[] args) {
      List<Rabbit> rabbits = new ArrayList<>();
      rabbits.add(new Rabbit());
      Comparator<Rabbit> c = (r1, r2) -> r1.id - r2.id;
      Collections.sort(rabbits, c);
   }
}
User avatar
Ursego
Site Admin
 
Posts: 143
Joined: 19 Feb 2013, 20:33



Ketones are a more high-octane fuel for your brain than glucose. Become a biohacker and upgrade yourself to version 2.0!



cron
Traffic Counter

eXTReMe Tracker