Java:Comparable VS Comparator

Java : Comparable vs Comparator

When your class implements Comparable, the compareTo method of the class is defining the "natural" ordering of that object. That method is contractually obligated (though not demanded) to be in line with other methods on that object, such as a 0 should always be returned for objects when the .equals() comparisons return true.

A Comparator is its own definition of how to compare two objects, and can be used to compare objects in a way that might not align with the natural ordering.

For example, Strings are generally compared alphabetically. Thus the "a".compareTo("b") would use alphabetical comparisons. If you wanted to compare Strings on length, you would need to write a custom comparator.

In short, there isn't much difference. They are both ends to similar means. In general implement comparable for natural order, (natural order definition is obviously open to interpretation), and write a comparator for other sorting or comparison needs.

When to use Comparable and Comparator

I would say that an object should implement Comparable if that is the clear natural way to sort the class, and anyone would need to sort the class would generally want to do it that way.

If, however, the sorting was an unusual use of the class, or the sorting only makes sense for a specific use case, then a Comparator is a better option.

Put another way, given the class name, is it clear how a comparable would sort, or do you have to resort to reading the javadoc? If it is the latter, odds are every future sorting use case would require a comparator, at which point the implementation of comparable may slow down users of the class, not speed them up.

Java: Comparable vs Comparator - Memory & Performance

That answer is incorrect.

Adding implemented interfaces or methods does not influence the memory required for individual instances of a class.

First of all, conceptually it doesn't make sense.

Implemented interfaces and methods are per-class information. Two instances of the same class will always implement exactly the same interfaces and have the exact same methods. As such, it makes no sense for the JVM to store that information per-object.

Second, you can easily verify that with sample code like this:

public class MyClass implements Comparable<MyClass> {

private final long l;

MyClass(long l) {this.l = l;}

@Override
public int compareTo(MyClass o) {
return 0;
}

public static void main(String[] args) {
long l = 0;
try {
var list = new ArrayList<MyClass>();
while (true) {
list.add(new MyClass(l++));
}
} catch (OutOfMemoryError e) {
System.out.println("Created " + l + " objects before things went south ...");
}
}
}

Running this with -Xmx32m using Java 11 will create about 200000 objects on each run for me (with slight variations, probably due to GC details).

Removing the Comparable interface and/or the compareTo method does not significantly change that value.

You can try adding additional fields or removing l, which will change the number.

Difference between Comparable and Comparator?

Not really.

Comparable and Comparator are generic interfaces that allow to compare instances of the type defined in the generic (subclasses included).

The main difference between them is that Comparable is directly implemented in the class which you want to compare objects.

Consequently, if you have a single way to compare instances from a class, that is that you have a natural order for them, Comparable is the right approach.

On the other hand, if you have multiple ways to compare instances from a class, Comparable is not enough.

You should use Comparators instead (if it doesn't exist a natural order) or use both (if it exists a natural order and some other kinds of order).


Example where Comparator can be useful in addition to Comparable :

The String class implements Comparable by comparing two strings lexicographically.
Suppose you need to sort a List of String according to a different rule : their length.

You will need to define a Comparator<String> that implements this rule such as :

public class StringLengthComparator implements Comparator<String> {

@Override
public int compare(String o1, String o2) {
return Integer.compare(o1.length(), o2.length());
}

}

Now you could sort Strings by using their natural order (using Comparable) :

List<String> strings = new ArrayList<>();
...
strings.sort();

But you could also use a specific Comparator<String> :

strings.sort(new StringLengthComparator());

Or without creating any class with a lambda:

strings.sort((o1,o2)->Integer.compare(o1.length(), o2.length()));

Example where Comparator should be used instead of Comparable :

Suppose you have an Account class that represents a bank account.

Functionally you don't have a natural order to sort them but you have instead multiple orders according to the client needs.
Making the class to implement Comparable would not make sense. But creating distinct Comparator<Account> would.


Case where only Comparator can be used :

If you want to define a order for instances of a class which you cannot change the source code (JDK class or third party class), Comparator is the way to follow.

When should a class be Comparable and/or Comparator?

The text below comes from Comparator vs Comparable

Comparable

A comparable object is capable of comparing itself with another object. The class itself must implements the java.lang.Comparable interface in order to be able to compare its instances.

Comparator

A comparator object is capable of comparing two different objects. The class is not comparing its instances, but some other class’s instances. This comparator class must implement the java.util.Comparator interface.

Why use comparable and comparator interface

A question such as "why use Comparator or Comparable rather than bubble sort?" really makes no sense. These are not alternatives to each other. Any sorting algorithm (including ones that you code yourself) needs a mechanism for defining the order of the objects. That is what the interfaces do: provide a mechanism for defining an ordering on objects.

If the question is "why use sorting methods built into the JDK rather than write your own?" then the answer is probably fairly obvious: it saves effort and complexity.

If the question is "when should you use Comparable and when should you use Comparator?" then I would suggest searching for one of the excellent primers on this topic. As a very simplified answer, Comparable is used to define a natural (default) ordering for objects within a class while Comparator is used to define a custom ordering to be passed to a method.

Note also that the Comparator interface has many very powerful methods for defining comparisons. Very little needs to be done manually now. For example, if you have a Person class you can sort a list with code such as:

Collection.sort(personList, Comparator
.comparingInt(Person::getAge)
.thenComparing(Person::getSurname));

In my view this code is better than the old mechanism of defining compareTo methods in a class as it hides away the implementation details of returning arbitrary integers representing comparison between fields and makes the intention pretty obvious. In fact it's enough of an improvement on older methods that in my own code I tend to avoid natural orderings altogether unless the class has an inherent obvious order that can be naturally represented as the difference between two integers (e.g. a class defining temperature or heights).

in what way is a comparator superior to comparable?

Comparable interface

The Comparable interface defines a type's natural ordering. Suppose you have a list of String or Integer objects; you can pass that list to

Collections.sort(list);

and you will have a sorted list. How? Because String and Integer both implement Comparable interface and the implementations of Comparable interface provide a natural ordering. Its like the class definition saying - "If you find a collection of objects of my type, order them according to the strategy I have defined in the compareTo method".

Now when you define your own type, you can define the natural ordering of the objects of your class by implementing the Comparable interface. See the Java documentation for more information on object ordering.

Comparator interface

The Comparator interface describes how to define custom strategies for object ordering. Suppose we have a simple Person type as below:

public class Person {
String name;

public Person(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

Now, by implementing the Comparator interface, you can write different strategies to order the instances of your Person type. For example, consider the two strategies for ordering Person objects given below:

class StrategyOne implements Comparator<Person> {

@Override
public int compare(Person p1, Person p2) {
return p1.getName().length() - p2.getName().length();
}

}

class StrategyTwo implements Comparator<Person> {

@Override
public int compare(Person p1, Person p2) {
return p1.getName().compareTo(p2.getName());
}

}

Here, StrategyOne will order Person objects based on the length of their names, and StrategyTwo will order Person objects based on lexicographic ordering of their names.

The ways to implement Comparator

As you can see, the concrete strategy classes are stateless, hence all instances are functionally equivalent. So, we just need a single instance of any concrete strategy class. Thus, it should be a singleton. Using anonymous classes will create a new instance each time the call is executed. Consider storing the object in a private static final field and reusing it by using static factory methods to access them [Effective Java]. For example, you can reuse the above two concrete strategies as below:

class Strategies {
private static final Comparator<Person> PERSON_NAME_LENGTH_COMPARATOR = new StrategyOne();

private static final Comparator<Person> PERSON_NAME_LEXICAL_COMPARATOR = new StrategyTwo();

public static Comparator<Person> personNameLengthComparator(){
return PERSON_NAME_LENGTH_COMPARATOR;
}

public static Comparator<Person> personNameLexicalComparator(){
return PERSON_NAME_LEXICAL_COMPARATOR;
}
}

Summary

To summarize, the Comparable interface is used to define the natural ordering of a class, and the Comparator interface is used to define particular strategies for object ordering.

When should I use Comparator vs Comparable?

There's a substantial distinction between the use cases for Comparator and Comparable.

Implementing the Comparable interface is suitable for objects that have a natural order in your domain model. I'm not sure whether animals have a natural order, but if it is the case from the perspective of how your application model the animals, that's fine - that's the way to go. Otherwise, your class should not implement Comparable.

It's not something opinion-based, documentation clearly defines when these interfaces are intended to be used.

Comparable:

This interface imposes a total ordering on the objects of each class
that implements it. This ordering is referred to as the class's
natural ordering
, and the class's compareTo method is referred to as
its natural comparison method.

Comparator:

Comparators can also be used to control the order of certain data structures (such as sorted sets or sorted maps), or to provide an ordering for collections of objects that don't have a natural ordering.

Another obvious distinction, that you can define as many flavors of comparators as you need. Which is handy when there's no one specific way to compare and sort the objects. And they must have more meaningful names than comparator.

Personally, I don't see a huge harm in defining a couple of comparators as public static final fields, as in your example. If you have a single class that manages the instances of this type - extract the comparators into that class, otherwise if these objects are ubiquitous and used in many places you can leave them right inside the POJO (that an opinion based part).

What is the difference between compare() and compareTo()?

From JavaNotes:

  • a.compareTo(b):

    Comparable interface : Compares values and returns an int which tells if the values compare less than, equal, or greater than.

    If your class objects have a natural order, implement the Comparable<T> interface and define this method. All Java classes that have a natural ordering implement Comparable<T> - Example: String, wrapper classes, BigInteger

  • compare(a, b):

    Comparator interface : Compares values of two objects. This is implemented as part of the Comparator<T> interface, and the typical use is to define one or more small utility classes that implement this, to pass to methods such as sort() or for use by sorting data structures such as TreeMap and TreeSet. You might want to create a Comparator object for the following:

    • Multiple comparisons. To provide several different ways to sort something. For example, you might want to sort a Person class by name, ID, age, height, ... You would define a Comparator for each of these to pass to the sort() method.
    • System class To provide comparison methods for classes that you have no control over. For example, you could define a Comparator for Strings that compared them by length.
    • Strategy pattern To implement a Strategy pattern, which is a situation where you want to represent an algorithm as an object that you can pass as a parameter, save in a data structure, etc.

If your class objects have one natural sorting order, you may not need compare().


Summary from http://www.digizol.com/2008/07/java-sorting-comparator-vs-comparable.html

Comparable
A comparable object is capable of comparing itself with another object.

Comparator
A comparator object is capable of comparing two different objects. The class is not comparing its instances, but some other class’s instances.


Use case contexts:

Comparable interface

The equals method and == and != operators test for equality/inequality, but do not provide a way to test for relative values.

Some classes (eg, String and other classes with a natural ordering) implement the Comparable<T> interface, which defines a compareTo() method.

You will want to implement Comparable<T> in your class if you want to use it with Collections.sort() or Arrays.sort() methods.

Defining a Comparator object

You can create Comparators to sort any arbitrary way for any class.

For example, the String class defines the CASE_INSENSITIVE_ORDER comparator.


The difference between the two approaches can be linked to the notion of:

Ordered Collection:

When a Collection is ordered, it means you can iterate in the collection in a specific (not-random) order (a Hashtable is not ordered).

A Collection with a natural order is not just ordered, but sorted. Defining a natural order can be difficult! (as in natural String order).


Another difference, pointed out by HaveAGuess in the comments:

  • Comparable is in the implementation and not visible from the interface, so when you sort you don't really know what is going to happen.
  • Comparator gives you reassurance that the ordering will be well defined.


Related Topics



Leave a reply



Submit