← Data abstraction CS Skill Tree

compareTo and compare, worked on a scoreboard

The lesson behind it is Implementing Comparable.

A Score class gets a natural order with compareTo, and a second order from a separate class with compare.

Your readiness not started

Your own marking on this problem.

The 2 orders

A Score holds a player's initials and a number of points.

OrderWhere it is writtenRule
the class's own order (its natural order)compareTo, inside Score, which implements Comparable<Score>more points first; on equal points, initials from A to Z
a second ordercompare, inside a separate class ByInitials, which implements Comparator<Score>initials from A to Z, points ignored

Both methods answer the same question with the sign of an int.

CallNegative0Positive
a.compareTo(b)a goes before ba tiea goes after b
c.compare(a, b)a goes before ba tiea goes after b

The natural order

public class Score implements Comparable<Score>
{
    private String initials;
    private int points;

    public Score(String initials, int points)
    {
        this.initials = initials;
        this.points = points;
    }

    public String getInitials()
    {
        return this.initials;
    }

    public int getPoints()
    {
        return this.points;
    }

    @Override
    public int compareTo(Score other)
    {
        int byPoints = Integer.compare(other.points, this.points);
        if (byPoints != 0)
        {
            return byPoints;
        }
        return this.initials.compareTo(other.initials);
    }

    @Override
    public String toString()
    {
        return this.initials + " " + this.points;
    }
}
LineWhy it is written that way
Integer.compare(other.points, this.points)Integer.compare(x, y) returns a negative number when x < y, 0 when they are equal, and a positive number when x > y. Putting other first flips the order, so more points comes first.
if (byPoints != 0)The points decide on their own unless they tie. Only a tie reaches the next key.
this.initials.compareTo(other.initials)String has its own compareTo, which orders by character codes. this first keeps A to Z.
other.pointsA private field of another Score is reachable here, because the code is inside the Score class.

The sort, and 4 sample comparisons

Arrays.sort(board) calls compareTo on pairs and arranges the array by the signs.

import java.util.Arrays;

// inside main
Score[] board = {
    new Score("JD", 90),
    new Score("AB", 75),
    new Score("KL", 90),
    new Score("ZZ", 75),
    new Score("AA", 100)
};
Arrays.sort(board);
System.out.println(Arrays.toString(board));
[AA 100, JD 90, KL 90, AB 75, ZZ 75]
CallKey that decidesResult
JD 90 against AA 100points, 90 against 1001, so JD goes after AA
JD 90 against KL 90points tie; initials, "JD".compareTo("KL")-1, so JD goes before KL
AB 75 against JD 90points, 75 against 901, so AB goes after JD
KL 90 against a second KL 90both keys tie0

A second order, from a separate class

A class can have only 1 compareTo. Any other order goes in a separate class that implements Comparator (a comparator). Its method takes both objects as parameters.

import java.util.Comparator;

public class ByInitials implements Comparator<Score>
{
    @Override
    public int compare(Score a, Score b)
    {
        return a.getInitials().compareTo(b.getInitials());
    }
}
compareTocompare
interface on the class lineComparable<Score>, on Score itselfComparator<Score>, on a separate class
parameters1, the other object; this is the first2, both objects
reaching the fieldsdirectly, because the code is inside Scorethrough getters, because the code is in another class
sorting with itArrays.sort(board)Arrays.sort(board, new ByInitials())

Sorting the same 5 scores with new ByInitials() prints:

[AA 100, AB 75, JD 90, KL 90, ZZ 75]

The cases, one call at a time

CallReturnsWhy
new Score("JD", 90).compareTo(new Score("KL", 90))-1points tie, and J is 1 before K
new Score("JD", 90).compareTo(new Score("AA", 100))1fewer points goes after
new Score("KL", 90).compareTo(new Score("KL", 90))0every key ties
new ByInitials().compare(new Score("ZZ", 75), new Score("AA", 100))25Z is 25 letters after A; only the sign matters
Subtracting to compare

return other.points - this.points; gives the right sign for small numbers. When the difference is too big for an int, the result wraps around to the other sign (integer overflow). With this.points equal to 1 and other.points equal to Integer.MIN_VALUE, the subtraction returns 2147483647, a positive number, and Integer.compare(other.points, this.points) returns -1. Integer.compare never overflows.

Drill

4 questions. Work each one on paper before you tap.

Level-up check

1. Predict the output. The board holds JD 90, AB 75, KL 90, ZZ 75, AA 100, in that order. What does Arrays.sort(board) followed by Arrays.toString(board) print?

2. The sign. What does new Score("AB", 75).compareTo(new Score("JD", 90)) return?

3. Spot the bug. Someone writes the first line of compareTo as int byPoints = Integer.compare(this.points, other.points); and leaves the rest alone. What does the sort from question 1 print?

4. Which method. A driver writes Arrays.sort(board, new ByInitials());. Which method does the sort call?

4 questions. 3 right is a pass.

Write it out

Not scored. Type it from memory, then compare. The box is scratch space and is not saved.

Write itThe keys in the other order

Rewrite Score's compareTo so that initials decide first, from A to Z, and on equal initials more points comes first.

Your checklist

Tick these when they are true.

Could you write this cold?

Blank file, no notes, 4 hours on a clock, and only the Java API documentation open. Say yes only when that is true. You can take it back at any time.

Rules on the practice APE
  • You change only the lines between //startMethod and //endMethod, and you keep each method header as given.
  • A private helper method or field of your own between the markers is fine.
  • No new imports.

The official site's Basic Strategies page says: You may receive points for compiling code but you WON’T receive points for code that does not compile.

The compareTo problem in each practice APE (on Student in Practice exams 1 and 2, on Luggage in Practice exam 3) and the StudentComparator problem in Practice exam 2 use these moves.

If a piece of this is shaky

Further reading