Primitive data types in Java can be directly compared using relational operators.
int valueA = 5, valueB = 10;
System.out.println(valueA > valueB); // false
System.out.println(valueA == valueB); // false
System.out.println(valueA < valueB); // true
char charA = 'm', charB = 'z';
// Compares underlying Unicode values
System.out.println(charA > charB); // false
System.out.println(charA == charB); // false
System.out.println(charA < charB); // true
For reference types, direct comparison is not meaningful. This article explores three approaches for comparing objects.
We'll use a GameTile class as an example, representing a tile with a value and a color.
class GameTile {
int value;
String color;
public GameTile(int value, String color) {
this.value = value;
this.color = color;
}
}
Overriding Object.equals()
All user-defined classes inherit from Object, which provides a default equals() method.
// Default implementation in Object class
public boolean equals(Object obj) {
return (this == obj); // Compares memory addresses
}
The default behavior compares object references. To compare based on internal state, we override equals().
class GameTile {
int value;
String color;
public GameTile(int value, String color) {
this.value = value;
this.color = color;
}
@Override
public boolean equals(Object otherObj) {
// Check if it's the same object
if (this == otherObj) {
return true;
}
// Check for null or incompatible type
if (otherObj == null || !(otherObj instanceof GameTile)) {
return false;
}
GameTile otherTile = (GameTile) otherObj;
// Compare based on value attribute
return value == otherTile.value;
}
}
Common override pattern:
- Return
trueif comparing the same object instance. - Return
falseif the arrgument isnull. - Return
falseif the argument is not of the correct type. - Perform field-by-field comparison as required.
Limitation: equals() only checks for equality, not relative ordering (greater than/less than).
Implementing the Comparable Interface
The java.lang.Comparable interface defines a natural ordering for a class. Implement compareTo().
public interface Comparable<T> {
// Returns:
// Negative: this object < specified object
// Zero: this object == specified object
// Positive: this object > specified object
int compareTo(T other);
}
class GameTile implements Comparable<GameTile> {
int value;
String color;
public GameTile(int value, String color) {
this.value = value;
this.color = color;
}
@Override
public int compareTo(GameTile otherTile) {
if (otherTile == null) {
return 1; // Treat non-null as greater than null
}
return this.value - otherTile.value;
}
}
Usage example:
public class ComparisonDemo {
public static void main(String[] args) {
GameTile tile1 = new GameTile(5, "Red");
GameTile tile2 = new GameTile(12, "Blue");
GameTile tile3 = new GameTile(5, "Red");
System.out.println(tile1.compareTo(tile3)); // 0 (equal)
System.out.println(tile2.compareTo(tile3)); // Positive (tile2 greater)
System.out.println(tile1.compareTo(tile2)); // Negative (tile1 less)
}
}
Creating a Custom Comparator
Define a separate class implementing java.util.Comparator to encapsulate a specific comparison logic.
import java.util.Comparator;
public interface Comparator<T> {
// Returns:
// Negative: first object < second object
// Zero: first object == second object
// Positive: first object > second object
int compare(T obj1, T obj2);
}
class TileValueComparator implements Comparator<GameTile> {
// Compares based on value, ignoring color.
// Treats null as the smallest possible value.
@Override
public int compare(GameTile tileA, GameTile tileB) {
if (tileA == tileB) {
return 0;
}
if (tileA == null) {
return -1;
}
if (tileB == null) {
return 1;
}
return tileA.value - tileB.value;
}
}
Usage example:
import java.util.Comparator;
public class ComparisonDemo {
public static void main(String[] args) {
GameTile tile1 = new GameTile(5, "Red");
GameTile tile2 = new GameTile(12, "Blue");
GameTile tile3 = new GameTile(5, "Red");
Comparator<GameTile> comp = new TileValueComparator();
System.out.println(comp.compare(tile1, tile3)); // 0
System.out.println(comp.compare(tile2, tile3)); // Positive
System.out.println(comp.compare(tile1, tile2)); // Negative
}
}
Comparison of Approaches
Object.equals(): Available in all classes by inheritance. Simple to override but limited to equality checks.Comparable.compareTo(): Defines a natural, internal ordering for a class. Requires modifyinng the class itself.Comparator.compare(): Provides external, flexible comparison logic without modifying the original class. Useful for multiple ordering criteria.