Static Method Reference as Comparator
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 target type is
Comparator<Integer>, requiringint compare(Integer a, Integer b). Integer.compare(int a, int b)is a two-argument static method returningint.- The shapes match: the method reference compiles.
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?
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:
- Static reference if
methodis astaticmethod onClassName(takes all arguments explicitly). - Instance reference if
methodis an instance method (the first argument becomes the receiver).
Source: JLS §15.13.
Misconception 2: writing int::compare instead of Integer::compare
Wrong mental model: “There is a
comparemethod on theinttype.”
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?
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
intvalues 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?
Why does the expression int::compare cause a compile error?
What does the following code print? Integer[] scores = {42, 7, 100, 3, 55}; Arrays.sort(scores, Integer::compare); System.out.println(Arrays.toString(scores));
What is the key behavioral difference between Integer::compare and Integer::compareTo when each is used as a Comparator<Integer>?
A student wants to sort an Integer[] called values in descending order. Which call is the preferred idiomatic form?