Ebooks

Java Comparable and Comparator

Java Comparable and Comparator tutorial shows how to compare objects in Java with Comparable and Comparator interfaces. Comparing two objects is essential when doing sorting.

When working with custom Java objects to perform comparisons, we can use Comparable or Comparator interfaces.

Java Comparable

The Comparable 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. The class's compareTo() method has to be implemented to provide the natural comparison.

Java Comparator

The Comparator interface imposes a total ordering on some collection of objects. Comparators can be passed to a sort method (such as Collections.sort() or Arrays.sort()) to allow precise control over the sort order. 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.

Comparable vs Comparator

The following two lists summarize the differences between the two interfaces.

Java Comparable

Java Comparator

Java built-in Comparator example

Java language offers some built-int Comparators.

JavaBuiltInComparatorEx.java
package com.zetcode;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;

public class JavaBuiltInComparatorEx {

    public static void main(String[] args) {

        List<String> words = new ArrayList<>();

        words.add("dog");
        words.add("pen");
        words.add("sky");
        words.add("rock");
        words.add("den");
        words.add("fountain");

        words.sort(Comparator.naturalOrder());
        words.forEach(System.out::println);

        words.sort(Comparator.reverseOrder());
        words.forEach(System.out::println);
    }
}

In the example, we sort an array of words in ascending and descending orders.

words.sort(Comparator.naturalOrder());

The Comparator.naturalOrder() returns a built-in natural order Comparator.

words.sort(Comparator.reverseOrder());

The Comparator.reverseOrder() returns a comparator that imposes the reverse of the natural ordering.

Comparator.comparingInt

The Comparator.comparingInt() method extracts the int sort key from the provided type and compares by that key.

JavaBuiltInComparatorEx2.java
package com.zetcode;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

class Person {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

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

    public void setAge(int age) {

        this.age = age;
    };

    public int getAge() {

        return this.age;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Person{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append('}');
        return sb.toString();
    }
}

public class JavaBuiltInComparatorEx2 {

    public static void main(String[] args) {

        Person p1 = new Person("Robert", 23);
        Person p2 = new Person("Monika", 18);
        Person p3 = new Person("Tom", 37);
        Person p4 = new Person("Elisabeth", 31);

        List<Person> vals = Arrays.asList( p1, p2, p3, p4 );

        vals.sort(Comparator.comparingInt(Person::getAge));
        vals.forEach(System.out::println);
    }
}

In the example, we compare Person objects by their age utilizing Comparator.comparingInt() method.

Person{name='Monika', age=18}
Person{name='Robert', age=23}
Person{name='Elisabeth', age=31}
Person{name='Tom', age=37}

The objects are sorted by age.

Multiple Comparators

With Comparator.thenComparing() method, we can use multiple comparators when sorting objects.

JavaMultipleComparatorsEx.java
package com.zetcode;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

// Comparing list of objects by multiple object fields

class Person {

    private String name;
    private int age;
    private String city;

    public Person(String name, int age, String city) {
        this.name = name;
        this.age = age;
        this.city = city;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Person{");
        sb.append("name='").append(name).append('\'');
        sb.append(", age=").append(age);
        sb.append(", city='").append(city).append('\'');
        sb.append('}');
        return sb.toString();
    }
}

public class JavaMultipleComparatorsEx {

    public static void main(String[] args) {

        List<Person> persons = Arrays.asList(
                new Person("Peter", 23, "New York"),
                new Person("Sarah", 13, "Las Vegas"),
                new Person("Lucy", 33, "Toronto"),
                new Person("Sarah", 21, "New York"),
                new Person("Tom", 18, "Toronto"),
                new Person("Robert", 23, "San Diego"),
                new Person("Lucy", 23, "Los Angeles"),
                new Person("Sam", 36, "Dallas"),
                new Person("Elisabeth", 31, "New York"),
                new Person("Ruth", 29, "New York"),
                new Person("Sarah", 41, "New York")
        );

        persons.sort(Comparator.comparing(Person::getName)
                .thenComparing(Person::getCity)
                .thenComparing(Person::getAge));

        persons.forEach(System.out::println);
    }
}

We have a list of Person objects. We compare the objects by their name, then by their city and finally by their age.

persons.sort(Comparator.comparing(Person::getName)
        .thenComparing(Person::getCity)
        .thenComparing(Person::getAge));

The Comparator.thenComparing() method allows us to apply multiply comparators to the sorting operation.

Person{name='Elisabeth', age=31, city='New York'}
Person{name='Lucy', age=23, city='Los Angeles'}
Person{name='Lucy', age=33, city='Toronto'}
Person{name='Peter', age=23, city='New York'}
Person{name='Robert', age=23, city='San Diego'}
Person{name='Ruth', age=29, city='New York'}
Person{name='Sam', age=36, city='Dallas'}
Person{name='Sarah', age=13, city='Las Vegas'}
Person{name='Sarah', age=21, city='New York'}
Person{name='Sarah', age=41, city='New York'}
Person{name='Tom', age=18, city='Toronto'}

This is the output.

Java custom Comparator

In the next example, we create a custom Comparator.

JavaCustomComparator.java
package com.zetcode;

import java.util.Arrays;
import java.util.List;

public class JavaCustomComparatorEx {

    public static void main(String[] args) {

        List<String> words = Arrays.asList("pen", "blue", "atom", "to",
                "ecclesiastical", "abbey", "car", "ten", "desk", "slim",
                "journey", "forest", "landscape", "achievement", "Antarctica");

        words.sort((e1, e2) -> e1.length() - e2.length());

        words.forEach(System.out::println);

        words.sort((e1, e2) ->  e2.length() - e1.length() );

        words.forEach(System.out::println);
    }
}

We have a list of words. This time we compare the words by their length.

words.sort((e1, e2) -> e1.length() - e2.length());

This custom comparator is used to sort the words by their size in ascending order.

words.sort((e1, e2) ->  e2.length() - e1.length() );

In the second case, the words are sorted in descending order.

to
pen
car
ten
blue
atom
desk
slim
abbey
forest
journey
landscape
Antarctica
achievement
ecclesiastical
ecclesiastical
achievement
Antarctica
landscape
journey
forest
abbey
blue
atom
desk
slim
pen
car
ten
to

This is the output.

Java custom Comparator II

In the following example, we create two custom comparators.

.java
package com.zetcode;

import java.util.Arrays;
import java.util.Comparator;

// Comparing objects with Comparator in array

class Car {

    private String name;
    private int price;

    public Car(String name, int price) {
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return name;
    }

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

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Car{");
        sb.append("name='").append(name).append('\'');
        sb.append(", price=").append(price);
        sb.append('}');
        return sb.toString();
    }
}

class CompareByPrice implements Comparator<Car> {

    @Override
    public int compare(Car c1, Car c2) {

        return c1.getPrice() - c2.getPrice();
    }
}

class CompareByName implements Comparator<Car> {

    @Override
    public int compare(Car c1, Car c2) {

        return c1.getName().compareTo(c2.getName());
    }
}

public class JavaCustomComparatorEx2 {

    public static void main(String[] args) {

        Car[] cars = {
                new Car("Volvo", 23400), new Car("Mazda", 13700),
                new Car("Porsche", 353800), new Car("Skoda", 8900),
                new Car("Volkswagen", 19900)
        };

        System.out.println("Comparison by price:");

        Arrays.sort(cars, new CompareByPrice());

        for (Car car : cars) {

            System.out.println(car);
        }

        System.out.println();

        System.out.println("Comparison by name:");

        Arrays.sort(cars, new CompareByName());

        for (Car car : cars) {

            System.out.println(car);
        }
    }
}

We have an array of Car objects. We create two custom comparators to compare the objects by their name and by their price.

class CompareByPrice implements Comparator<Car> {

    @Override
    public int compare(Car c1, Car c2) {

        return c1.getPrice() - c2.getPrice();
    }
}
...
Arrays.sort(cars, new CompareByPrice());

The custom CompareByPrice comparator implements the Comparator interface; forcing us to implement the compare() method. Our implementation compares the car objects by their price.

class CompareByName implements Comparator<Car> {

    @Override
    public int compare(Car c1, Car c2) {

        return c1.getName().compareTo(c2.getName());
    }
}
...
Arrays.sort(cars, new CompareByName());

In the second case, we are comparing car objects by their name.

Comparison by price:
Car{name='Skoda', price=8900}
Car{name='Mazda', price=13700}
Car{name='Volkswagen', price=19900}
Car{name='Volvo', price=23400}
Car{name='Porsche', price=353800}

Comparison by name:
Car{name='Mazda', price=13700}
Car{name='Porsche', price=353800}
Car{name='Skoda', price=8900}
Car{name='Volkswagen', price=19900}
Car{name='Volvo', price=23400}

This is the outplut.

Java Comparable example

In the following example, we compare objects with Comparable.

JavaComparableEx.java
package com.zetcode;

import java.util.Arrays;
import java.util.Comparator;

class Card implements Comparable<Card> {

    @Override
    public int compareTo(Card o) {

        return Comparator.comparing(Card::getValue)
                .thenComparing(Card::getSuit)
                .compare(this, o);
    }

    public enum Suits {
        SPADES,
        CLUBS,
        HEARTS,
        DIAMONDS
    }

    public enum Values {
        TWO,
        THREE,
        FOUR,
        FIVE,
        SIX,
        SEVEN,
        EIGHT,
        NINE,
        TEN,
        JACK,
        QUEEN,
        KING,
        ACE,
    }

    private Suits suit;
    private Values value;

    public Card(Values value, Suits suit) {
        this.value = value;
        this.suit = suit;
    }

    public Values getValue() {
        return value;
    }

    public Suits getSuit() {
        return suit;
    }

    public void showCard() {
       
        value = getValue();
        suit = getSuit();

        System.out.println(value + " of " + suit);
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("Card{");
        sb.append("suit=").append(suit);
        sb.append(", value=").append(value);
        sb.append('}');
        return sb.toString();
    }
}

public class JavaComparableEx {

    public static void main(String[] args) {

        Card[] cards = {
                new Card(Card.Values.KING, Card.Suits.DIAMONDS),
                new Card(Card.Values.FIVE, Card.Suits.HEARTS),
                new Card(Card.Values.ACE, Card.Suits.CLUBS),
                new Card(Card.Values.NINE, Card.Suits.SPADES),
                new Card(Card.Values.JACK, Card.Suits.SPADES),
                new Card(Card.Values.JACK, Card.Suits.DIAMONDS),};

        for (Card card: cards) {

            System.out.println(card);
        }
    }
}

We have a list of Card objects. Each card has a value and belongs to a suit. We implement to Comparable interface to provide some natural ordering to the objects of Card class.

@Override
public int compareTo(Card o) {

    return Comparator.comparing(Card::getValue)
            .thenComparing(Card::getSuit)
            .compare(this, o);
}

We implement the compareTo() method. We compare the cards first by their value and then by their suit.

Card{suit=HEARTS, value=FIVE}
Card{suit=SPADES, value=NINE}
Card{suit=SPADES, value=JACK}
Card{suit=DIAMONDS, value=JACK}
Card{suit=DIAMONDS, value=KING}
Card{suit=CLUBS, value=ACE}

This is the output.

In this tutorial, we have shown how to compare objects in Java using Comparable and Comparator. You might also be interested in the related tutorials: Java tutorial, Reading text files in Java, and Filtering a list in Java.