CSCD 211 › lessons

Prerequisite: order, built from "which of these two comes first?"

This is the rung above equality, and the last one before Week 1. In 210 you wrote methods that take arguments and return a value. This lesson takes one specific method, the one that answers "which comes first," and shows you that it has a hidden shape: a set of rules any order must obey, the same rules discrete math gives for an order relation. By the end the never-subtract rule will not be a rule you memorize; it will be the only thing that could possibly be true.

Quick check before you climb (retrieval first)

Answer from memory before reading on. No stakes; retrieving 210 now is what makes the new idea land.

Check yourself. A 210 method returns an int. A caller uses that int in two different ways: it can care about the actual number, or it can care only about whether the number is negative, zero, or positive. Name one method you have seen where only the sign matters.

The point

By the end you can say why the answer to "which comes first" is a sign and not a magnitude, you can state the two rules any order must obey and recognize them as the order-relation axioms from discrete math, and you can prove, from the language rulebook, why subtracting two ints to compare them is not a shortcut but a bug. One climb, each rung a click.

The problem (sit with this before reading on)

You want to order students by account balance, who owes the most first, so you reach for the shortest thing that could work:

Comparator<Student> byBalance =
    (a, b) -> a.balanceCents() - b.balanceCents();

A balance is signed: a positive value is money owed, a negative one is a refund the school owes the student. It passes every test you try. Then one night, on two accounts left near opposite ends of the int range by a bad import, the sort lists a large debt below a large refund, an order that is plainly wrong, and only on that pair. The code never changed. The reason it was always capable of this, and only now showed it, is the whole point of this lesson.

Check yourself. The expression is a.balanceCents() - b.balanceCents(). Without knowing the fix yet, name the one thing about int subtraction that could make this return the wrong sign.

Rung 1: the answer is a sign, not a magnitude

Ask "which of these two comes first," and the smallest honest answer is one of three things: the first comes before the second, they tie, or the first comes after the second. Java packs those three answers into one int and uses only its sign: negative means before, zero means tie, positive means after. The size of the number carries no meaning. A result of -1 and a result of -2000000000 say the identical thing, the first comes before the second.

This is why a comparison is allowed to return any negative number, not just -1. The caller, a sort or a TreeSet, reads the sign and throws the magnitude away. If you ever find yourself caring how big the result is, you have misread the contract.

Collectible insight. An order is a sign, not a magnitude. Trust whether the number is negative, zero, or positive; never trust its size.

Check yourself. A comparison returns -2000000000 for one pair and -1 for another. In one sentence, do those two results tell the caller different things?

Rung 2: an order has a shape, and the shape has rules

An order is not any function that returns a sign. To deserve the name, it has to be consistent with itself. Discrete math gives three axioms for an order relation, and two of them do the visible work here:

The third axiom, reflexivity, is the quiet one: an item compared with itself ties, so compareTo returns zero. Break antisymmetry or transitivity and a sort cannot finish honestly: it might place an item both ahead of and behind another, or build a cycle with no first element. These rules are not Java's invention. They are the axioms discrete math gives for an order relation, a partial order (Rosen, Discrete Mathematics, section 9.6). The contract you will sign in Week 1, Comparable, is those axioms written as a method.

Going deeper (optional). A partial order is reflexive, antisymmetric, and transitive; it becomes a total order when every pair is comparable, which is what a sort needs (Rosen 9.6). If you are in discrete math, this is the partial-orderings section, and the cs-to-math cross-tree link collects it.

Collectible insight. The Comparable contract is the order-relation axioms: reflexivity, antisymmetry, and transitivity. You met them in discrete math as the rules of a partial order.

Check yourself. A comparison says a comes before b and b comes before c, but also says c comes before a. Which rule is broken, and why can a sort not finish?

Rung 3: the click, why subtraction is a real bug, not a style choice

Now the opening bug. The language rulebook settles it in two lines. First, the Java Language Specification says an int is a 32-bit two's-complement value, holding only the range from -2147483648 to 2147483647 (JLS 4.2.1). Second, it says, in these words, that "the integer operators do not indicate overflow or underflow in any way" (JLS 4.2.2). Put those together: when you compute a.balanceCents() - b.balanceCents() and the two values are far enough apart in sign, a large positive minus a large negative, the true result does not fit in 32 bits, so it silently wraps around, and a difference that should be positive comes back negative. Java does not warn you. By the rulebook, it cannot.

A flipped sign is not a small error. It makes the order loop: the comparison can say the first value comes before the second, the second before the third, and the third before the first, the exact cycle from Rung 2. That is a transitivity violation, and a sort handed a cycle has no honest first element to place. On a short list it returns a quietly wrong order; on a longer one the library sort detects the broken contract and throws. (The same flipped sign can also make the order claim both values come first, which breaks antisymmetry.) The math axiom and the language rule describe the same failure from two sides, so the bug is not a Java quirk to work around; it is the order relation breaking its own definition.

The fix removes subtraction entirely:

Comparator<Student> byBalance =
    (a, b) -> Integer.compare(a.balanceCents(), b.balanceCents());

Integer.compare decides the sign by comparing, never by subtracting, so nothing can overflow and the sign is always honest. Not every field is at risk: a count like enrollment is never negative, so subtracting two enrollments could not overflow and would in fact give the right answer. A balance is different, because it is signed, and a large positive minus a large negative is exactly the case that wraps. So the rule is not "this line is broken today," it is "subtraction is unsafe the moment a field can be negative or approach the ends of the int range, and it corrupts the order with no warning when it does." You write Integer.compare everywhere so you never have to track which fields are safe. Lab 1 will check this on extreme values, which is how the habit gets proven general instead of merely assumed.

Collectible insight. Subtraction overflow makes the order loop, a transitivity violation, not a Java quirk (and the same flip can break antisymmetry too). The rulebook says the int operators never signal overflow (JLS 4.2.2), so Integer.compare is the only safe sign.

Check yourself. In one sentence that names overflow and transitivity, say why (a, b) -> a.balanceCents() - b.balanceCents() can break a sort.

Rung 4: why Week 1 makes this a contract, not a method

In 210, a method that returned a value was just a method. Week 1 takes the one method that answers "which comes first" and makes it a contract, Comparable, because everything built on top, a TreeSet, a sorted catalog, a binary search, trusts it to obey those axioms. A method that quietly violates the order contract can corrupt a TreeSet far from where the mistake lives, silently dropping an element it mistakes for a duplicate. That is why Week 1 treats the order axioms as law and why this prerequisite ends here, one step below the first lecture, with the shape of an order already in your hands.

Check yourself (competency close). Finish in your own words: "The answer to which comes first is a , not a . An order must be , , and , and subtracting ints to compare can break ___ because ."

What you collected

Walk into Tuesday with the shape of an order already in hand, so Comparable reads as the contract you expected rather than a new rule to take on faith.