← Skill tree CS Skill Tree 0 CSCD211

Static Method Reference as Comparator

Textbook: BJP (Reges and Stepp)

Before You Start

Check each box you can do from memory. A box you cannot check yet is not a problem; it points you to a quick refresher, not a grade.

Not sure? Take the 60-second self-check.

1. Lambda as Comparator. Write the lambda that replaces (a, b) -> Integer.compare(a, b) for sorting integers.

Check

The lambda itself IS (a, b) -> Integer.compare(a, b). It is already a correct comparator. The question for this lesson is whether we can say the same thing more concisely.

2. Static method call. What does Integer.compare(3, 5) return?

Check

A negative integer. Integer.compare(x, y) returns negative when x < y, zero when x == y, and positive when x > y. Integer.compare(3, 5) returns a negative value.

3. Method reference syntax. How is a static method reference written in Java?

Check

ClassName::staticMethodName, with no parentheses and no arguments. For example, Integer::compare.


Try This First

The lambda (a, b) -> Integer.compare(a, b) sorts integers ascending. It takes two arguments, calls one static method, and returns its result directly. Before reading further: guess a shorter way to write this that names the method without wrapping it in a lambda body.

Check

Integer::compare: a static method reference that means exactly the same thing as (a, b) -> Integer.compare(a, b) but in one token.


What You Need To Walk In With

The Insight: A method reference is a lambda with no new body: it names an existing method and says “use that method’s code as the body.” When a lambda body is nothing but a single method call that passes all parameters through unchanged, a method reference is the exact compressed form. The code communicates intent more directly, because the method name says what comparison is being performed.

To use this fluently, you need to be able to read ClassName::staticMethodName and know it is not a call but a reference to a function: the arguments will be supplied later by whoever calls the comparator. You also need to know the difference between a static method (owned by the class) and an instance method (owned by an object), because that distinction determines which form of method reference is appropriate. You can: write Integer::compare as a drop-in comparator for ascending integer sort, recognize why int::compare fails to compile, state that the method reference and the equivalent lambda produce identical bytecode, and choose Comparator.reverseOrder() for descending order without resorting to argument-swapping tricks.


How It Works

Static method reference syntax

ClassName::staticMethodName is a static method reference. It names a static method and packages it as a value whose type is inferred from the target context.

For Arrays.sort(intArray, Integer::compare):

The canonical forms for numeric ascending sorts:

Arrays.sort(integerArr, Integer::compare);    // Integer[]
Arrays.sort(longArr,    Long::compare);       // Long[]
Arrays.sort(doubleArr,  Double::compare);     // Double[]
Arrays.sort(boolArr,    Boolean::compare);    // Boolean[]

These are the safe, idiomatic replacements for the subtraction trick ((a, b) -> a - b), which is a dangerous antipattern covered in The Subtraction Trick and Why It Overflows.

Why this replaces the subtraction trick

Integer::compare is overflow-safe, reads as intent (it names the comparison being performed), and is one token. The subtraction trick (a, b) -> a - b is three tokens, produces wrong results on overflow, and hides the intent inside arithmetic.

Descending sort

For descending integer order, do not swap arguments in a lambda. Use Comparator.reverseOrder():

// Descending on Integer[]
Arrays.sort(integerArr, Comparator.reverseOrder());

// Or swap arguments in the static reference (works but less readable):
Arrays.sort(integerArr, (a, b) -> Integer.compare(b, a));

Comparator.reverseOrder() returns a comparator that imposes the reverse of the natural order, which is the standard idiom.


Worked Example: Predict Then Check

Integer[] scores = {42, 7, 100, 3, 55};
Arrays.sort(scores, Integer::compare);
System.out.println(Arrays.toString(scores));

Predict the output.

Reasoning

Integer::compare is equivalent to (a, b) -> Integer.compare(a, b), which sorts ascending. The smallest value (3) comes first; the largest (100) comes last.

Show answer
[3, 7, 42, 55, 100]

The array is sorted ascending. Arrays.sort calls Integer.compare(a, b) on pairs and reorders in place.


Quick check

Check your understanding

Which call correctly sorts a Long[] called times in ascending order using a static method reference?

Tier 2 · BJP (Reges and Stepp), Ch 10


Common Misconceptions

Misconception 1: Integer::compare and Integer::compareTo are interchangeable

Wrong mental model: “Both are Integer comparison shortcuts; I can use either.”

Why it matters (on a small example):

// Integer::compare is a STATIC method reference:
// int compare(int x, int y)
// shapes to Comparator<Integer> as: compare(a, b) -> Integer.compare(a, b)

// Integer::compareTo is an INSTANCE method reference:
// int compareTo(Integer o)  (single arg; receiver is first comparand)
// shapes to Comparator<Integer> as: compare(a, b) -> a.compareTo(b)

Both happen to produce the same sort order for Comparator<Integer>. The distinction matters when selecting the right form for a custom type or when the shapes do not align.

How to correct: Read ClassName::method as:

Source: JLS §15.13.


Misconception 2: writing int::compare instead of Integer::compare

Wrong mental model: “There is a compare method on the int type.”

Why it breaks: int is a primitive type; it has no methods. Methods exist only on classes. The static comparison method is on the wrapper class Integer. Writing int::compare is a compile error: “cannot find symbol.”

How to correct: Use the wrapper class: Integer::compare for int, Long::compare for long, Double::compare for double.

Source: JLS §4.2.


Quick check

Check your understanding

A student writes Arrays.sort(nums, int::compare); where nums is an Integer[]. What happens and why?

Tier 1 · BJP (Reges and Stepp), Ch 10


Formal Definition and Interface Contract

From JLS §15.13 (Method Reference Expressions):

A method reference expression is used to refer to the invocation of a method without actually invoking it. [...] The type of a method reference expression is a functional interface.

From the Java 25 Integer API:

public static int compare(int x, int y)

Compares two int values numerically. The value returned is identical to what would be returned by: Integer.valueOf(x).compareTo(Integer.valueOf(y))


Mental Model

Think of a static method reference as a pointer to a pre-written comparison function. Instead of writing the body of the comparator yourself ((a, b) -> Integer.compare(a, b)), you point at the function that already exists and has that body. The method reference Integer::compare says: “use the compare function on the Integer class.” The compiler resolves this to the same bytecode as the lambda.


Connections

Within CSCD 210/211: The subtraction trick overflow pitfall explains why (a, b) -> a - b is unsafe. Integer::compare is the canonical one-token fix for that pitfall.

Looking back: Writing a Comparator as a Lambda allows writing a comparator body inline. A static method reference is a further compression: the body reduces to a single method call, and the method reference names that call without wrapping it in (a, b) -> ... syntax.

Looking ahead: Instance Method Reference on the First Argument covers Comparator.comparing(Player::getLastName), where the method reference extracts a key from the first argument. Comparator.comparing by Key covers Comparator.comparing in depth.


Go Deeper (optional)

None of what follows is needed to write correct, professional Java code. If you are comfortable with Integer::compare and can explain why it beats the subtraction trick, you have everything required. This section is for when you want to understand where these ideas fit in the larger landscape of computing.

The method reference as a first-class function. When you write Integer::compare, you are treating a method as a value: something that can be stored in a variable, passed as an argument, and returned from a method. This is what it means for a language to support first-class functions. Java gained this with lambda expressions in Java 8, but the concept is older: Lisp had first-class functions in 1958, and ML made the idea central to typed functional programming in the 1970s. The method reference is Java’s surface syntax for the same idea, constrained to the shape of a named functional interface.

The Strategy pattern. Passing Integer::compare to Arrays.sort is an instance of the Strategy design pattern: the sorting algorithm (the context) is separated from the comparison rule (the strategy), so either can vary independently. The same Arrays.sort algorithm works for ascending integers, descending strings, and custom objects sorted by any field, because the comparison rule is plugged in, not hard-coded. Parnas (1972) identified this separation-of-concerns principle as the foundation of modular design; the Strategy pattern is the object-oriented formalization.

Five types of method references. Bloch’s Effective Java (Item 43) catalogs five categories: static (Integer::compare), bound instance (instant.method), unbound instance (Type::instanceMethod), constructor (Type::new), and super-instance. This leaf covers only the static form. The unbound instance form, where the first argument of the comparator becomes the receiver of the method call, is the next concept in this sequence.

Order theory. A Comparator is more than a sorting trick: it defines a binary relation on a set. When that relation satisfies antisymmetry, transitivity, and totality, it imposes a strict total order, and the axioms you must satisfy to implement a correct comparator are exactly those axioms. The Comparator contract in the Java API is a direct translation of the mathematical definition. Breaking the contract (for example, by returning an inconsistent sign on repeated calls) produces undefined sorting behavior because the sort algorithm assumes the axioms hold.

In professional Java code, Integer::compare, Long::compare, and Double::compare appear in virtually every codebase that sorts primitive-typed data. Recognizing and writing them fluently is a baseline expectation in industry code reviews.


Practice

Level 1

Write the sort call that sorts an Integer[] called ages in ascending order using a static method reference.

Show answer
Arrays.sort(ages, Integer::compare);

Level 2

A student writes Arrays.sort(longs, long::compare);. Explain the compile error and provide the correct form.

Show answer

long is a primitive type; it has no methods. The static comparison method is on the wrapper class Long. The correct form:

Arrays.sort(longs, Long::compare);

(Where longs is a Long[], not a long[]; Arrays.sort(long[], Comparator) does not exist because arrays of primitives use Arrays.sort(long[]) with no comparator.)


Level 3

A Score class has a getPoints() method returning int. Write three equivalent sort calls for Score[] scores in ascending order by points, using (a) subtraction, (b) a lambda with Integer.compare, and (c) a method reference with Comparator.comparingInt. State which of (a), (b), (c) is preferred and why.

Show answer
// (a) subtraction -- WRONG: overflow risk
Arrays.sort(scores, (x, y) -> x.getPoints() - y.getPoints());

// (b) lambda with Integer.compare -- correct
Arrays.sort(scores, (x, y) -> Integer.compare(x.getPoints(), y.getPoints()));

// (c) comparingInt -- correct and idiomatic
Arrays.sort(scores, Comparator.comparingInt(Score::getPoints));

(c) is preferred for readability: Comparator.comparingInt(Score::getPoints) directly expresses “compare by the integer key getPoints” without any arithmetic. Option (a) is wrong due to overflow. Option (b) is correct but more verbose than (c).

Source: Bloch, Effective Java, Item 43.


Check Yourself

Close the notes and answer each one from memory, then reveal it. Pulling an idea back from memory is one of the strongest ways to make it stick.

Check your understanding

Which of the following is the correct static method reference to sort an Integer[] in ascending order?

Tier 1 · BJP (Reges and Stepp), Ch 10

Why does the expression int::compare cause a compile error?

Tier 1 · BJP (Reges and Stepp), Ch 10

What does the following code print? Integer[] scores = {42, 7, 100, 3, 55}; Arrays.sort(scores, Integer::compare); System.out.println(Arrays.toString(scores));

Tier 2 · BJP (Reges and Stepp), Ch 10

What is the key behavioral difference between Integer::compare and Integer::compareTo when each is used as a Comparator<Integer>?

Tier 2 · BJP (Reges and Stepp), Ch 10

A student wants to sort an Integer[] called values in descending order. Which call is the preferred idiomatic form?

Tier 3 · BJP (Reges and Stepp), Ch 10