eddiewastaken
eddiewastaken

Reputation: 832

How to implement Comparator using compareTo()

I have a class, Card:

public class Card implements Comparable<Card> {
    public enum rank {TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE};
    private rank Rank;
    public enum suit {CLUBS, DIAMONDS, HEARTS, SPADES};
    private suit Suit;
}

Which I need to perform two tasks on. First - Make the Card class Comparable, so that compareTo can be used to sort the cards into ascending order. I've done this here:

@Override
public int compareTo(Card other) {
    if(this.Rank != other.Rank) {
        if(this.Rank.ordinal() > other.Rank.ordinal()) {
        return 1;
        }
        else if(this.Rank.ordinal() < other.Rank.ordinal()) {
            return -1;
        }
        else {
            return 0;
        }
    }
    else {
        if(this.Suit.ordinal() > other.Suit.ordinal()) {
        return 1;
        }
        else if(this.Suit.ordinal() < other.Suit.ordinal()) {
            return -1;
        }
        else {
            return 0;
        }
    }
}

and secondly - Add a Comparator class as a nested classes of the Card class called CompareDescending. This should be used to sort the cards into descending order. I've done this here, and it works well:

public static class CompareDescending implements Comparator<Card> {
    @Override
    public int compare(Card card, Card other) {
        if(card.Rank != other.Rank) {
            if(card.Rank.ordinal() < other.Rank.ordinal()) {
            return 1;
            }
            else if(card.Rank.ordinal() > other.Rank.ordinal()) {
                return -1;
            }
            else {
                return 0;
            }
        }
        else {
            if(card.Suit.ordinal() < other.Suit.ordinal()) {
            return 1;
            }
            else if(card.Suit.ordinal() > other.Suit.ordinal()) {
                return -1;
            }
            else {
                return 0;
            }
        }
    }
}

However, I wondered if I've gone about this the wrong way. Should my nested Comparator class use the compareTo() function inside it? Is this better practice?

Upvotes: 1

Views: 777

Answers (2)

Oleg Cherednik
Oleg Cherednik

Reputation: 18245

You custom comparator is very complex. Comparator class provides suitable method to make custom comparator easier. Additionally, enum implements natural order comparator.

To create descending order comparator you do not have to rewrite existed asceding order comparator. Just call Comparator.reverseOrder() before.

class Card implements Comparable<Card> {

    public static final Comparator<Card> SORT_ASC = Comparator.<Card, Rank>comparing(card -> card.rank).thenComparing(card -> card.suit);
    public static final Comparator<Card> SORT_DESC = SORT_ASC.reversed();

    private Rank rank;
    private Suit suit;

    @Override
    public int compareTo(Card card) {
        return SORT_ASC.compare(this, card);
    }

    public enum Rank {}
    public enum Suit {}
}

Demo

List<Card> cards = Collections.emptyList();
List<Card> asc = cards.stream().sorted(Card.SORT_ASC).collect(Collectors.toList());
List<Card> desc = cards.stream().sorted(Card.SORT_DESC).collect(Collectors.toList());

Upvotes: 0

Andreas
Andreas

Reputation: 159086

Your compareTo is overly complex.

enum implements a compareTo for you, for ordering enum values in the declared order, i.e. by ordinal.

That means your code can simply be:

@Override
public int compareTo(Card other) {
    int cmp = this.Rank.compareTo(other.Rank);
    if (cmp == 0)
        cmp = this.Suit.compareTo(other.Suit);
    return cmp;
}

To compare in reverse order, you simply flip the objects being compared, i.e. instead of a.compareTo(b) you write b.compareTo(a), so a descending Comparator implementation would be:

public static class CompareDescending implements Comparator<Card> {
    @Override
    public int compare(Card card, Card other) {
        return other.compareTo(card); // descending
    }
}

It is a good idea to document (comment) that the comparison is reversed, because casual review of the code might very easily miss that. Here, I have documented it by simply commenting that the compare is "descending".

Upvotes: 5

Related Questions