Comparable vs. Compparator

Posted by dotancohen on Sat, 22 Jun 2019 00:51:46 +0200

1. Prototype of two interfaces

In Java, Comparable and Comparator interfaces are used for comparison. So what's the difference between the two interfaces in actual use? Now let's analyze it with examples.

Let's first look at the prototype of the two interfaces in JDK.

package java.lang;
import java.util.*;

public interface Comparable<T> {
    public int compareTo(T o);
}
package java.util;

import java.io.Serializable;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.function.ToDoubleFunction;
import java.util.Comparators;

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

2. Use of Comparable

Generally speaking, Comparable is for sorting a collection of classes, so it is usually the class itself that needs sorting to implement the Comparable interface. In other words, if a class implements the Comparable interface, then an array or List of that class can be sorted.

For a simple example:

public class Employee implements Comparable<Employee> {

    private String name;
    private int salary;

    public Employee(String name, int salary) {
        this.name = name;
        this.salary = salary;
    }

    @Override
    public int compareTo(Employee other) {
        return this.salary - other.salary;
    }

    @Override
    public String toString() {
        return "name is: " + name + ", salary is: " + salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }
}

Implement Employee Collection Sorting in Client:

public class CompareTest {

    public static List<Employee> genList() {
        Employee e1 = new Employee("aaa",100);
        Employee e2 = new Employee("bbb",150);
        Employee e3 = new Employee("ccc", 80);
        List<Employee> list = new ArrayList();

        list.add(e1);
        list.add(e2);
        list.add(e3);

        return list;
    }

    public static void t1() {
        List<Employee> list = genList();
        //Collections.sort(list); both can be done, in which the source code is called list.sort(null)
        list.sort(null);
        System.out.println(list);
    }

    public static void main(String[] args) {
        t1();
    }

}

run the client's code, and the final output is:

[name is: ccc, salary is: 80, name is: aaa, salary is: 100, name is: bbb, salary is: 150]

Because Employee implements the Comparable interface, it can sort the Employee array directly.

3.Comparator interface usage

Many times we can't modify classes, or the cost of such modifications is too high, but we want to sort them. What can we do? At this point, the Comparator interface is in use.
For example, we will modify the Employee class slightly to not implement the Comparable interface, and add the final keyword:

public final class Employee  {

    private String name;
    private int salary;

    public Employee(String name, int salary) {
        this.name = name;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "name is: " + name + ", salary is: " + salary;
    }
    ...This is omitted.get/set
}

At this point, we obviously can't modify the Employee class. But we still need to sort them. What should we do?

If before jdk8, use anonymous internal classes:

    public static void test() {
        List<Employee> list = genList();
        Collections.sort(list, new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return o1.getSalary() - o2.getSalary();
            }
        });
        System.out.println(list);
    }

After jdk8, lambda expressions can be used:

    public static void test() {
        List<Employee> list = genList();
        Comparator<Employee> comparator = (Employee e1, Employee e2) -> e1.getSalary() - e2.getSalary();
        list.sort(comparator);
        System.out.println(list);
    }

If this method run s, the output is as follows:

[name is: ccc, salary is: 80, name is: aaa, salary is: 100, name is: bbb, salary is: 150]

Students may notice that there is only one compareTo method to implement in the Comparable interface, while Comparator has two methods, but we only implement one method, so what about another method?
In fact, it's very simple, because the other method is the equals method. All classes inherit the Object class, and the Object class implements the equals method, so it doesn't matter if we don't implement the equals method here!

4.Comparator implementation comparison

Comparator compare method is still implemented in many ways. Now let's explain.

4.1 Traditional Anonymous Internal Classes

Prior to JDK8, anonymous internal classes were commonly used to implement:

        Collections.sort(list, new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return o1.getSalary() - o2.getSalary();
            }
        });

4.2 lambda expression

After JDK8, lambda expressions can be used:

list.sort((Employee e1, Employee e2) -> e1.getSalary() - e2.getSalary());

4.3 Streamlined lambda Expressions

We further simplify the expression by not specifying a type definition, because the compiler itself can make type judgments.

list.sort((e1, e2) -> e1.getSalary() - e2.getSalary());

4.4 Use Comparator. comparison

When we use the lambda expression above, IDE prompts us that we can be replaced with comparator.comparing Int

list.sort(Comparator.comparing(employee -> employee.getSalary()));

4.5 References using static methods

Double colons in java are method references. :: It is a usage of lambda introduced in JDK8 to express references, such as the reference String::valueOf of static methods, such as the reference of constructors, ArrayList::new.

list.sort(Comparator.comparing(Employee::getSalary));

4.6 Sort Inversion

Many times, you want to reverse the order, or reverse the order:

list.sort(Comparator.comparing(Employee::getSalary).reversed());

4.7 Multiple Conditional Combination Sorting

        list.sort((e1, e2) -> {
            if(e1.getSalary() != e2.getSalary()) {
                return e1.getSalary() - e2.getSalary();
            } else {
                return e1.getName().compareTo(e2.getName());
            }
        });

4.8 Starting with JDK 8, we can now chain together multiple Comparator s to build more complex comparative logic.

list.sort(Comparator.comparing(Employee::getSalary).thenComparing(Employee::getName));

Topics: Java Lambda JDK