Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Java comparators: tie-breakers, overflow and sorted-key identity

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

A Comparator defines an ordering relation; sorted collections use a zero comparison to decide that two keys occupy one ordering position.

Java 8+. This is a complete program using JDK classes.

Preserve distinct jobs

A dispatch comparator that compares only priority merges two distinct jobs at the same priority when used in a TreeSet. Sorting a list preserves both values, but the sorted set interprets equality under the comparator as one membership slot. The same comparator can therefore have different consequences depending on its consumer.

Use a stable identifier as a tie-breaker when both jobs must remain. The identifier must itself have a total order and a defined duplicate policy. A changing priority field makes a mutable key dangerous after insertion; a sorted structure does not continuously rebuild its internal path when a field changes.

Do not compare integer priorities by subtraction. Extreme inputs can overflow and reverse the intended sign. Comparator.comparingInt, Integer.compare and explicit thenComparing steps state the policy without relying on a difference fitting in int.

Test the relation

Check equal values, reverse pairs and triples when reviewing a custom comparator. Inconsistent or non-transitive results can invalidate sorted algorithms. The program deliberately compares the broken policy with an identifier-aware policy so the lost member is visible without a timing measurement.

Working program

Java
import java.util.Comparator;
import java.util.TreeSet;
public class DispatchJobOrdering {
    static final class Job {
        final String id; final int priority;
        Job(String id, int priority) { this.id=id; this.priority=priority; }
    }
    public static void main(String[] args) {
        Comparator<Job> priorityOnly = Comparator.comparingInt(job -> job.priority);
        TreeSet<Job> merged = new TreeSet<>(priorityOnly);
        merged.add(new Job("D-11", 3)); merged.add(new Job("D-12", 3));
        TreeSet<Job> distinct = new TreeSet<>(priorityOnly.thenComparing(job -> job.id));
        distinct.addAll(merged); distinct.add(new Job("D-12", 3));
        System.out.println(merged.size());
        System.out.println(distinct.size());
        System.out.println(Integer.compare(Integer.MIN_VALUE, Integer.MAX_VALUE));
    }
}

Output

Output
1
2
-1

Costs and boundaries

Each TreeSet insertion uses O(log n) comparisons. The comparator may add string comparison work proportional to a differing prefix; O(log n) does not mean character work disappears. Membership storage is O(n).

Common Mistakes

  • A priority tie is not necessarily object equality.
  • Changing a comparison field after insertion can corrupt lookup assumptions.
  • Subtracting two int values can overflow.

Read next

Set equivalence, Comparator sorting.

Continue with the new boundary checks

Continue with Java LinkedList sort: stable order needs temporary array work.

java
comparator-contracts
Storage details