Sort Objects Using Comparable in Java
Problem
Once a class implements Comparable and defines its natural ordering, Collections.sort() can sort a whole list of them directly, with no extra sorting logic supplied at the call site.
Given a list of Product objects that implement Comparable by price, sort the list into ascending order.
Java Program
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Product implements Comparable<Product> {
String name;
double price;
Product(String name, double price) {
this.name = name;
this.price = price;
}
public int compareTo(Product other) {
return Double.compare(this.price, other.price);
}
@Override
public String toString() {
return name + ": $" + price;
}
}
public class SortObjectsUsingComparable {
public static void main(String[] args) {
List<Product> products = new ArrayList<>();
products.add(new Product("Laptop", 999.99));
products.add(new Product("Mouse", 19.99));
products.add(new Product("Keyboard", 49.99));
Collections.sort(products); // relies on Product's own compareTo(), no separate Comparator needed
for (Product p : products) {
System.out.println(p);
}
}
}Output
Core Logic
Because Product already defines its own natural ordering through compareTo(), Collections.sort() can rearrange the whole list without being told how to compare two products.
- 1
class Product implements Comparable<Product>definescompareTo()usingDouble.compare(this.price, other.price), making price the natural ordering. - 2
Collections.sort(products)is called with no second argument — noComparatoris supplied, so it falls back to each element's owncompareTo(). - 3Internally, the sort algorithm calls
compareTo()between pairs ofProductobjects to decide their relative order, the same way it would compareIntegers orStrings. - 4After sorting, the list itself is rearranged in place —
productsnow holds the same three objects in ascending price order.
[Laptop, Mouse, Keyboard], sorting by each product's compareTo() reorders them to [Mouse, Keyboard, Laptop], from cheapest to most expensive.Key Point: Collections.sort(list) with no second argument only compiles when the list's element type implements Comparable — this is the compiler enforcing that a natural ordering actually exists before it lets you sort by it.
Why: Collections.sort() uses a comparison-based sort with O(n log n) time; sorting an ArrayList in place needs no extra storage proportional to the list's size.
Key Concepts
Approach 2: Java 8
import java.util.List;
import java.util.stream.Collectors;
class ProductRecord implements Comparable<ProductRecord> {
String name;
double price;
ProductRecord(String name, double price) {
this.name = name;
this.price = price;
}
public int compareTo(ProductRecord other) {
return Double.compare(this.price, other.price);
}
@Override
public String toString() {
return name + ": $" + price;
}
}
public class SortObjectsUsingComparableStream {
public static void main(String[] args) {
List<ProductRecord> products = List.of(
new ProductRecord("Laptop", 999.99),
new ProductRecord("Mouse", 19.99),
new ProductRecord("Keyboard", 49.99)
);
// Relies on ProductRecord's own compareTo(), collecting into a new sorted list
List<ProductRecord> sorted = products.stream()
.sorted()
.collect(Collectors.toList());
for (ProductRecord p : sorted) {
System.out.println(p);
}
}
}
Output
Core Logic
Streaming the list and calling the no-argument sorted() relies on Product's own compareTo() the same way Collections.sort() does, but produces a new sorted list instead of reordering the original.
- 1
products.stream()opens a stream over the list's elements. - 2
.sorted()with no arguments sorts using each element's natural ordering —Product's owncompareTo()— exactly likeCollections.sort(products)does. - 3
.collect(Collectors.toList())gathers the sorted stream into a brand-new List. - 4Unlike the primary approach's in-place
Collections.sort(), the originalproductslist is left in its original order.
[Laptop, Mouse, Keyboard] by their natural (price) ordering collects into [Mouse, Keyboard, Laptop], the same ascending order the primary approach produces.Key Point: sorted() with no arguments only compiles when the stream's element type implements Comparable, the same compile-time requirement Collections.sort(list) has — the difference here is purely in-place mutation versus a freshly-collected list.
Why: sorted() still performs the same comparison-based sort as Collections.sort(), and collect() additionally builds a new list to hold the result, unlike the primary approach's in-place sort.