Skip to content

Using Comparable VS Comparator

Brendan Cheong Ern Jie edited this page Oct 11, 2021 · 10 revisions

What's the Difference Between Comparable and Comparator?

Comparable Interface

The comparable interface compares elements based on a single elements. This means that we need to override the compareTo method within the class that is implementing the comparable interface. We usually use comparable when we only want to sort something based on one attribute. For example, I only want to sort oranges based on sourness so I use comparable.

Comparator Interface

The comparator interface can compares elements based on multiple elements. We do not need to implement the Comparable interface in the class that is using the comparator. Instead, its best to create a separate class that implements the comparable interface and overrides the method compare. We usually use the comparator interface when we want to sort something based on many attributes or more than one condition. For example, I need to output two sorted lists of oranges, one sorted according to sourness the other according to cuteness. In this case, I need 2 comparators as a single compareTo method is not enough (and I can't have more than 1 compareTo methods)

Comparable Comparator
Comparable provides a single sorting sequence. The Comparator provides multiple sorting sequences.
Comparable affects the original class, i.e., the actual class is modified. Comparator doesn't affect the original class, i.e., the actual class is not modified.
Comparable provides compareTo() method to sort elements. Comparator provides compare() method to sort elements.
Comparable is present in java.lang package. A Comparator is present in the java.util package.
We can sort the list elements of Comparable type by Collections.sort(List) method. We can sort the list elements of Comparator type by Collections.sort(List, Comparator) method.

Prompt

Sort an Orange object based on its sourness-cuteness ratio.

class Orange {
    // non-private for the sake of example
    double cuteness;
    double sourness;

    Orange(double sourness, double cuteness) {
        this.sourness = sourness;
        this.cuteness = cuteness;
    }

    @Override
    public String toString() {
        return String.format("Orange with %f sourness-cuteness ratio", this.sourness / this.cuteness);
    }
}

Using Comparable

Modify the Orange class to make it comparable.

class Orange implements Comparable<Orange> {
    // non-private for the sake of example
    double cuteness;
    double sourness;

    Orange(double sourness, double cuteness) {
        this.sourness = sourness;
        this.cuteness = cuteness;
    }

    @Override
    public int compareTo(Orange other) { // must return either 1 or -1 and 0 iff they are equal
        if (this.sourness / this.cuteness > other.sourness / other.cuteness) {
            return 1;
        } else if (this.sourness / this.cuteness < other.sourness / other.cuteness) {
            return -1;
        } else {
            return 0;
        }
    }

    @Override
    public String toString() {
        return String.format("Orange with %f sourness-cuteness ratio", this.sourness / this.cuteness);
    }
}

// Example run
orangesList.sort(null);

Using Comparator

Use the original Orange class, but implement a new comparator class instead.

import java.util.Comparator;

class OrangeComparator implements Comparator<Orange> {
    public int compare(Orange o1, Orange o2) {
        return (int) (o1.sourness / o1.cuteness - o2.sourness / o2.cuteness);
    }
}

// Example run
orangesList.sort(new OrangeComparator());

Misc

List<Orange> orangesList = new ArrayList<Orange>(Arrays.asList(
    new Orange(7, 5), new Orange(6, 2), new Orange(-1, 2.5), new Orange(7, 4)));

Using HashMaps and Comparators

Lets say you have an abstract class called "Fruit" an it has many subclasses like "Orange", "Durian", "Watermelon" etc. If you have many subclasses and want to rank each of them accordingly when sorting, you can use a HashMap to do so! For example, "Durian" is the king of fruits, so its always at the top of the stack, followed by "Oranges" second, "Watermelons" third etc.

class FruitComparator implements Comparator<Fruit> {
    public int compare(Fruit fruit1, Fruit fruit2) {
        Map<String, Integer> rankings = new HashMap<>();
        rankings.put("Durian", 1);
        rankings.put("Oranges", 2);
        rankings.put("Watermelons", 3);
        Integer fruit1Rank = rankings.get(fruit1.name);
        Integer fruit2Rank = rankings.get(fruit2.name);
        return fruit1Rank.compareTo(fruit2Rank);
    }
}
Clone this wiki locally