The Comparator interface is used to define custom sorting logic for objects. It provides a way to sort objects in different orders without modifying the actual class.
1. Comparator Interface Basics
Comparator vs Comparable
import java.util.*; public class ComparatorBasics { static class Student implements Comparable<Student> { String name; int age; double gpa; public Student(String name, int age, double gpa) { this.name = name; this.age = age; this.gpa = gpa; } // Natural ordering - by name @Override public int compareTo(Student other) { return this.name.compareTo(other.name); } @Override public String toString() { return String.format("Student{name='%s', age=%d, gpa=%.2f}", name, age, gpa); } } public static void main(String[] args) { List<Student> students = new ArrayList<>(); students.add(new Student("John", 20, 3.5)); students.add(new Student("Alice", 22, 3.8)); students.add(new Student("Bob", 19, 3.2)); students.add(new Student("Carol", 21, 3.9)); System.out.println("=== Natural Ordering (by name - Comparable) ==="); Collections.sort(students); students.forEach(System.out::println); System.out.println("\n=== Custom Ordering (by age - Comparator) ==="); // Using anonymous inner class Comparator<Student> byAge = new Comparator<Student>() { @Override public int compare(Student s1, Student s2) { return Integer.compare(s1.age, s2.age); } }; Collections.sort(students, byAge); students.forEach(System.out::println); System.out.println("\n=== Custom Ordering (by GPA - Comparator) ==="); // Using lambda expression Comparator<Student> byGpa = (s1, s2) -> Double.compare(s1.gpa, s2.gpa); Collections.sort(students, byGpa); students.forEach(System.out::println); } } Basic Comparator Implementations
import java.util.*; public class BasicComparators { public static void main(String[] args) { List<String> names = Arrays.asList("John", "Alice", "Bob", "Carol", "David"); System.out.println("Original list: " + names); // 1. Natural ordering Collections.sort(names); System.out.println("Natural order: " + names); // 2. Reverse order using Comparator Collections.sort(names, Comparator.reverseOrder()); System.out.println("Reverse order: " + names); // 3. Case-insensitive order List<String> mixedCase = Arrays.asList("apple", "Banana", "cherry", "Date"); Collections.sort(mixedCase, String.CASE_INSENSITIVE_ORDER); System.out.println("Case-insensitive: " + mixedCase); // 4. By string length Collections.sort(names, Comparator.comparingInt(String::length)); System.out.println("By length: " + names); // 5. By string length then natural order Collections.sort(names, Comparator.comparingInt(String::length) .thenComparing(Comparator.naturalOrder()) ); System.out.println("By length then natural: " + names); demonstrateIntegerComparators(); } public static void demonstrateIntegerComparators() { System.out.println("\n=== Integer Comparators ==="); List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9, 3); System.out.println("Original: " + numbers); // Natural order Collections.sort(numbers); System.out.println("Natural: " + numbers); // Reverse order Collections.sort(numbers, Comparator.reverseOrder()); System.out.println("Reverse: " + numbers); // Custom: even numbers first, then odd Comparator<Integer> evenFirst = (a, b) -> { boolean aEven = a % 2 == 0; boolean bEven = b % 2 == 0; if (aEven && !bEven) return -1; if (!aEven && bEven) return 1; return Integer.compare(a, b); }; Collections.sort(numbers, evenFirst); System.out.println("Even first: " + numbers); } } 2. Comparator Factory Methods
Java 8+ Comparator Factory Methods
import java.util.*; import java.util.Comparator; public class ComparatorFactoryMethods { static class Product { String name; double price; int quantity; String category; public Product(String name, double price, int quantity, String category) { this.name = name; this.price = price; this.quantity = quantity; this.category = category; } @Override public String toString() { return String.format("Product{name='%-12s', price=$%-6.2f, quantity=%-3d, category='%s'}", name, price, quantity, category); } // Getters for method references public String getName() { return name; } public double getPrice() { return price; } public int getQuantity() { return quantity; } public String getCategory() { return category; } } public static void main(String[] args) { List<Product> products = Arrays.asList( new Product("Laptop", 999.99, 10, "Electronics"), new Product("Mouse", 29.99, 50, "Electronics"), new Product("Book", 19.99, 100, "Education"), new Product("Chair", 149.99, 15, "Furniture"), new Product("Table", 299.99, 8, "Furniture"), new Product("Pen", 2.99, 200, "Stationery") ); System.out.println("=== Comparator.comparing() ==="); // 1. Comparing by field using method reference Comparator<Product> byName = Comparator.comparing(Product::getName); products.sort(byName); System.out.println("Sorted by name:"); products.forEach(System.out::println); // 2. Comparing by price Comparator<Product> byPrice = Comparator.comparing(Product::getPrice); products.sort(byPrice); System.out.println("\nSorted by price:"); products.forEach(System.out::println); // 3. Comparing by price (reversed) Comparator<Product> byPriceDesc = Comparator.comparing(Product::getPrice).reversed(); products.sort(byPriceDesc); System.out.println("\nSorted by price (descending):"); products.forEach(System.out::println); // 4. Natural order and null handling demonstrateNaturalOrderAndNulls(); // 5. Multiple field comparison demonstrateMultipleFieldComparison(products); } public static void demonstrateNaturalOrderAndNulls() { System.out.println("\n=== Natural Order and Null Handling ==="); List<String> namesWithNulls = Arrays.asList("John", null, "Alice", null, "Bob", "Carol"); // This would throw NullPointerException // Collections.sort(namesWithNulls); // Handle nulls - nulls first namesWithNulls.sort(Comparator.nullsFirst(Comparator.naturalOrder())); System.out.println("Nulls first: " + namesWithNulls); // Handle nulls - nulls last namesWithNulls.sort(Comparator.nullsLast(Comparator.naturalOrder())); System.out.println("Nulls last: " + namesWithNulls); } public static void demonstrateMultipleFieldComparison(List<Product> products) { System.out.println("\n=== Multiple Field Comparison ==="); // Sort by category, then by price within category Comparator<Product> byCategoryThenPrice = Comparator.comparing(Product::getCategory) .thenComparing(Product::getPrice); products.sort(byCategoryThenPrice); System.out.println("Sorted by category then price:"); products.forEach(System.out::println); // Sort by category, then by price descending, then by name Comparator<Product> complexComparator = Comparator.comparing(Product::getCategory) .thenComparing(Comparator.comparing(Product::getPrice).reversed()) .thenComparing(Product::getName); products.sort(complexComparator); System.out.println("\nSorted by category, price (desc), name:"); products.forEach(System.out::println); } } 3. Custom Comparator Implementations
Advanced Custom Comparators
import java.util.*; public class CustomComparators { static class Employee { String firstName; String lastName; String department; int salary; int yearsOfExperience; public Employee(String firstName, String lastName, String department, int salary, int yearsOfExperience) { this.firstName = firstName; this.lastName = lastName; this.department = department; this.salary = salary; this.yearsOfExperience = yearsOfExperience; } @Override public String toString() { return String.format("Employee{%s %s, Dept: %-12s, Salary: $%-6d, Exp: %d years}", firstName, lastName, department, salary, yearsOfExperience); } // Getters public String getFirstName() { return firstName; } public String getLastName() { return lastName; } public String getDepartment() { return department; } public int getSalary() { return salary; } public int getYearsOfExperience() { return yearsOfExperience; } } public static void main(String[] args) { List<Employee> employees = Arrays.asList( new Employee("John", "Doe", "Engineering", 80000, 3), new Employee("Alice", "Smith", "Engineering", 95000, 5), new Employee("Bob", "Johnson", "Marketing", 70000, 2), new Employee("Carol", "Williams", "Engineering", 85000, 4), new Employee("David", "Brown", "Marketing", 75000, 6), new Employee("Eve", "Davis", "HR", 65000, 1) ); System.out.println("=== Custom Comparator Examples ==="); // 1. Sort by last name then first name Comparator<Employee> byLastNameThenFirstName = Comparator.comparing(Employee::getLastName) .thenComparing(Employee::getFirstName); employees.sort(byLastNameThenFirstName); System.out.println("\nBy last name then first name:"); employees.forEach(System.out::println); // 2. Sort by department then salary (descending) Comparator<Employee> byDeptThenSalaryDesc = Comparator.comparing(Employee::getDepartment) .thenComparing(Comparator.comparing(Employee::getSalary).reversed()); employees.sort(byDeptThenSalaryDesc); System.out.println("\nBy department then salary (descending):"); employees.forEach(System.out::println); // 3. Complex custom logic: Seniority based on experience and salary Comparator<Employee> bySeniority = (e1, e2) -> { // Primary: years of experience int expCompare = Integer.compare(e2.yearsOfExperience, e1.yearsOfExperience); // desc if (expCompare != 0) return expCompare; // Secondary: salary return Integer.compare(e2.salary, e1.salary); // desc }; employees.sort(bySeniority); System.out.println("\nBy seniority (experience desc, salary desc):"); employees.forEach(System.out::println); // 4. Custom department ordering demonstrateCustomDepartmentOrdering(employees); } public static void demonstrateCustomDepartmentOrdering(List<Employee> employees) { System.out.println("\n=== Custom Department Ordering ==="); // Define custom department priority Map<String, Integer> deptPriority = Map.of( "Engineering", 1, "Marketing", 2, "HR", 3 ); Comparator<Employee> byCustomDeptOrder = (e1, e2) -> { int dept1Priority = deptPriority.getOrDefault(e1.department, Integer.MAX_VALUE); int dept2Priority = deptPriority.getOrDefault(e2.department, Integer.MAX_VALUE); return Integer.compare(dept1Priority, dept2Priority); }; employees.sort(byCustomDeptOrder); System.out.println("By custom department order (Engineering > Marketing > HR):"); employees.forEach(System.out::println); } } // Specialized comparators as separate classes class EmployeeComparators { // Static comparator instances public static final Comparator<Employee> BY_FULL_NAME = Comparator.comparing(Employee::getLastName) .thenComparing(Employee::getFirstName); public static final Comparator<Employee> BY_SALARY_DESC = Comparator.comparing(Employee::getSalary).reversed(); public static final Comparator<Employee> BY_EXPERIENCE_THEN_SALARY = Comparator.comparing(Employee::getYearsOfExperience) .thenComparing(Employee::getSalary); // Factory method for department-based comparator public static Comparator<Employee> byDepartmentWithCustomOrder(List<String> departmentOrder) { Map<String, Integer> orderMap = new HashMap<>(); for (int i = 0; i < departmentOrder.size(); i++) { orderMap.put(departmentOrder.get(i), i); } return (e1, e2) -> { int order1 = orderMap.getOrDefault(e1.getDepartment(), Integer.MAX_VALUE); int order2 = orderMap.getOrDefault(e2.getDepartment(), Integer.MAX_VALUE); return Integer.compare(order1, order2); }; } } 4. Comparator with Collections
Using Comparator with Different Collections
import java.util.*; public class ComparatorWithCollections { static class Person { String name; int age; String city; public Person(String name, int age, String city) { this.name = name; this.age = age; this.city = city; } @Override public String toString() { return String.format("Person{name='%s', age=%d, city='%s'}", name, age, city); } public String getName() { return name; } public int getAge() { return age; } public String getCity() { return city; } } public static void main(String[] args) { List<Person> people = Arrays.asList( new Person("John", 25, "New York"), new Person("Alice", 30, "Boston"), new Person("Bob", 22, "Chicago"), new Person("Carol", 35, "New York"), new Person("David", 28, "Boston") ); System.out.println("=== Comparator with List ==="); // Sort list by age people.sort(Comparator.comparing(Person::getAge)); System.out.println("Sorted by age:"); people.forEach(System.out::println); System.out.println("\n=== Comparator with TreeSet ==="); // TreeSet with custom comparator TreeSet<Person> peopleByCity = new TreeSet<>( Comparator.comparing(Person::getCity) .thenComparing(Person::getName) ); peopleByCity.addAll(people); System.out.println("TreeSet sorted by city then name:"); peopleByCity.forEach(System.out::println); System.out.println("\n=== Comparator with PriorityQueue ==="); // PriorityQueue with custom comparator (max-heap by age) PriorityQueue<Person> agePriorityQueue = new PriorityQueue<>( Comparator.comparing(Person::getAge).reversed() ); agePriorityQueue.addAll(people); System.out.println("PriorityQueue by age (descending):"); while (!agePriorityQueue.isEmpty()) { System.out.println(agePriorityQueue.poll()); } System.out.println("\n=== Comparator with Arrays.sort ==="); Person[] peopleArray = people.toArray(new Person[0]); // Sort array by name Arrays.sort(peopleArray, Comparator.comparing(Person::getName)); System.out.println("Array sorted by name:"); Arrays.stream(peopleArray).forEach(System.out::println); demonstrateMapSorting(); } public static void demonstrateMapSorting() { System.out.println("\n=== Sorting Map Entries ==="); Map<String, Integer> population = new HashMap<>(); population.put("New York", 8419000); population.put("Los Angeles", 3980000); population.put("Chicago", 2716000); population.put("Houston", 2328000); population.put("Phoenix", 1690000); System.out.println("Original map: " + population); // Sort by key List<Map.Entry<String, Integer>> entriesByKey = new ArrayList<>(population.entrySet()); entriesByKey.sort(Map.Entry.comparingByKey()); System.out.println("Sorted by key: " + entriesByKey); // Sort by value (descending) List<Map.Entry<String, Integer>> entriesByValue = new ArrayList<>(population.entrySet()); entriesByValue.sort(Map.Entry.comparingByValue(Comparator.reverseOrder())); System.out.println("Sorted by value (desc): " + entriesByValue); // Using TreeMap with custom comparator TreeMap<String, Integer> sortedByKeyLength = new TreeMap<>( Comparator.comparingInt(String::length) .thenComparing(Comparator.naturalOrder()) ); sortedByKeyLength.putAll(population); System.out.println("TreeMap sorted by key length: " + sortedByKeyLength); } } 5. Advanced Comparator Techniques
Chaining and Composition
import java.util.*; import java.util.function.Function; public class AdvancedComparatorTechniques { static class Student { String name; int mathScore; int scienceScore; int englishScore; boolean isInternational; public Student(String name, int mathScore, int scienceScore, int englishScore, boolean isInternational) { this.name = name; this.mathScore = mathScore; this.scienceScore = scienceScore; this.englishScore = englishScore; this.isInternational = isInternational; } public double getAverageScore() { return (mathScore + scienceScore + englishScore) / 3.0; } public int getTotalScore() { return mathScore + scienceScore + englishScore; } @Override public String toString() { return String.format("Student{name='%-10s', avg=%-5.1f, total=%-3d, intl=%b}", name, getAverageScore(), getTotalScore(), isInternational); } } public static void main(String[] args) { List<Student> students = Arrays.asList( new Student("John", 85, 90, 88, false), new Student("Alice", 92, 95, 90, false), new Student("Bob", 78, 82, 80, true), new Student("Carol", 95, 88, 92, false), new Student("David", 88, 85, 90, true), new Student("Eve", 90, 92, 95, true) ); System.out.println("=== Advanced Comparator Techniques ==="); // 1. Dynamic comparator builder Comparator<Student> byMathScore = createFieldComparator(Student::getMathScore); students.sort(byMathScore); System.out.println("\nBy math score:"); students.forEach(System.out::println); // 2. Chaining with conditional logic Comparator<Student> byInternationalThenScore = (s1, s2) -> { // International students first if (s1.isInternational && !s2.isInternational) return -1; if (!s1.isInternational && s2.isInternational) return 1; // Then by total score (descending) return Integer.compare(s2.getTotalScore(), s1.getTotalScore()); }; students.sort(byInternationalThenScore); System.out.println("\nBy international status then total score (desc):"); students.forEach(System.out::println); // 3. Composite comparator with weights demonstrateWeightedComparator(students); // 4. Null-safe chaining demonstrateNullSafeChaining(); } // Generic method to create field comparators public static <T, U extends Comparable<? super U>> Comparator<T> createFieldComparator( Function<? super T, ? extends U> fieldExtractor) { return Comparator.comparing(fieldExtractor); } public static void demonstrateWeightedComparator(List<Student> students) { System.out.println("\n=== Weighted Score Comparator ==="); // Weighted average: math 40%, science 35%, english 25% Comparator<Student> byWeightedScore = (s1, s2) -> { double weighted1 = s1.mathScore * 0.4 + s1.scienceScore * 0.35 + s1.englishScore * 0.25; double weighted2 = s2.mathScore * 0.4 + s2.scienceScore * 0.35 + s2.englishScore * 0.25; return Double.compare(weighted2, weighted1); // descending }; students.sort(byWeightedScore); System.out.println("By weighted score (math 40%, science 35%, english 25%):"); students.forEach(System.out::println); } public static void demonstrateNullSafeChaining() { System.out.println("\n=== Null-Safe Comparator Chaining ==="); List<String> stringsWithNulls = Arrays.asList("apple", null, "banana", null, "cherry"); // Null-safe comparator chain Comparator<String> nullSafeComparator = Comparator.nullsLast( Comparator.comparingInt(String::length) .thenComparing(Comparator.naturalOrder()) ); stringsWithNulls.sort(nullSafeComparator); System.out.println("Null-safe chained comparator: " + stringsWithNulls); // Multiple null handling in custom objects List<Student> studentsWithNulls = Arrays.asList( new Student("John", 85, 90, 88, false), null, new Student("Alice", 92, 95, 90, false), null, new Student("Bob", 78, 82, 80, true) ); Comparator<Student> nullSafeStudentComparator = Comparator.nullsFirst(Comparator.comparing(Student::getTotalScore)); studentsWithNulls.sort(nullSafeStudentComparator); System.out.println("\nStudents with nulls (nulls first, then by total score):"); studentsWithNulls.forEach(System.out::println); } } // Comparator utility class class ComparatorUtils { // Creates a comparator that compares by multiple fields with custom directions @SafeVarargs public static <T> Comparator<T> chainedComparator(Comparator<T>... comparators) { return Arrays.stream(comparators) .reduce(Comparator::thenComparing) .orElse((a, b) -> 0); } // Creates a comparator for a specific field with null handling public static <T, U extends Comparable<? super U>> Comparator<T> nullSafeComparator( Function<? super T, ? extends U> fieldExtractor, boolean nullsFirst) { Comparator<T> comparator = Comparator.comparing(fieldExtractor); return nullsFirst ? Comparator.nullsFirst(comparator) : Comparator.nullsLast(comparator); } // Creates a comparator that uses a custom transformation public static <T, U extends Comparable<? super U>> Comparator<T> transformingComparator( Function<? super T, ? extends U> transformer) { return Comparator.comparing(transformer); } // Example usage public static void main(String[] args) { List<String> words = Arrays.asList("apple", "banana", "cherry", "date"); // Chain multiple comparators Comparator<String> complexComparator = chainedComparator( Comparator.comparingInt(String::length), Comparator.comparing(String::toLowerCase) ); words.sort(complexComparator); System.out.println("Chained comparator result: " + words); } } 6. Real-World Examples
E-commerce Product Sorting
import java.util.*; public class ECommerceProductSorting { static class Product { String id; String name; double price; double rating; int sales; String category; Date createdDate; public Product(String id, String name, double price, double rating, int sales, String category, Date createdDate) { this.id = id; this.name = name; this.price = price; this.rating = rating; this.sales = sales; this.category = category; this.createdDate = createdDate; } @Override public String toString() { return String.format("Product{id='%s', name='%-15s', price=$%-6.2f, rating=%-3.1f, sales=%-4d, category='%s'}", id, name, price, rating, sales, category); } // Getters public String getId() { return id; } public String getName() { return name; } public double getPrice() { return price; } public double getRating() { return rating; } public int getSales() { return sales; } public String getCategory() { return category; } public Date getCreatedDate() { return createdDate; } } // Predefined sorting strategies public static class ProductSorters { public static final Comparator<Product> BY_PRICE_ASC = Comparator.comparing(Product::getPrice); public static final Comparator<Product> BY_PRICE_DESC = Comparator.comparing(Product::getPrice).reversed(); public static final Comparator<Product> BY_RATING_DESC = Comparator.comparing(Product::getRating).reversed(); public static final Comparator<Product> BY_POPULARITY = Comparator.comparing(Product::getSales).reversed() .thenComparing(BY_RATING_DESC); public static final Comparator<Product> BY_NEWEST = Comparator.comparing(Product::getCreatedDate).reversed(); public static Comparator<Product> byCategoryThen(Criteria criteria) { Comparator<Product> baseComparator = getComparatorForCriteria(criteria); return Comparator.comparing(Product::getCategory) .thenComparing(baseComparator); } public static Comparator<Product> createCustomComparator( boolean priceAsc, boolean ratingDesc, boolean newestFirst) { Comparator<Product> comparator = Comparator.comparing(Product::getPrice); if (!priceAsc) comparator = comparator.reversed(); if (ratingDesc) { comparator = comparator.thenComparing(BY_RATING_DESC); } if (newestFirst) { comparator = comparator.thenComparing(BY_NEWEST); } return comparator; } private static Comparator<Product> getComparatorForCriteria(Criteria criteria) { switch (criteria) { case PRICE_ASC: return BY_PRICE_ASC; case PRICE_DESC: return BY_PRICE_DESC; case RATING: return BY_RATING_DESC; case POPULARITY: return BY_POPULARITY; case NEWEST: return BY_NEWEST; default: return BY_PRICE_ASC; } } } enum Criteria { PRICE_ASC, PRICE_DESC, RATING, POPULARITY, NEWEST } public static void main(String[] args) throws Exception { List<Product> products = createSampleProducts(); System.out.println("=== E-Commerce Product Sorting ==="); // Different sorting strategies System.out.println("\n--- By Price (Low to High) ---"); products.sort(ProductSorters.BY_PRICE_ASC); products.forEach(System.out::println); System.out.println("\n--- By Rating (High to Low) ---"); products.sort(ProductSorters.BY_RATING_DESC); products.forEach(System.out::println); System.out.println("\n--- By Popularity (Sales then Rating) ---"); products.sort(ProductSorters.BY_POPULARITY); products.forEach(System.out::println); System.out.println("\n--- By Category then Price ---"); products.sort(ProductSorters.byCategoryThen(Criteria.PRICE_ASC)); products.forEach(System.out::println); System.out.println("\n--- Custom: Price Desc, Then Rating, Then Newest ---"); Comparator<Product> customComparator = ProductSorters.createCustomComparator(false, true, true); products.sort(customComparator); products.forEach(System.out::println); demonstrateDynamicSorting(products); } private static List<Product> createSampleProducts() throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd"); return Arrays.asList( new Product("P1", "Laptop", 999.99, 4.5, 150, "Electronics", sdf.parse("2024-01-15")), new Product("P2", "Smartphone", 699.99, 4.2, 300, "Electronics", sdf.parse("2024-02-20")), new Product("P3", "Headphones", 149.99, 4.7, 200, "Electronics", sdf.parse("2024-01-10")), new Product("P4", "T-Shirt", 19.99, 4.0, 500, "Clothing", sdf.parse("2024-03-01")), new Product("P5", "Jeans", 49.99, 4.3, 350, "Clothing", sdf.parse("2024-02-15")), new Product("P6", "Book", 12.99, 4.8, 100, "Books", sdf.parse("2024-01-20")), new Product("P7", "Desk Lamp", 29.99, 4.1, 80, "Home", sdf.parse("2024-03-05")) ); } private static void demonstrateDynamicSorting(List<Product> products) { System.out.println("\n=== Dynamic Sorting Based on User Preference ==="); // Simulate user preferences Map<String, String> userPreferences = new HashMap<>(); userPreferences.put("sortBy", "price"); userPreferences.put("order", "desc"); userPreferences.put("category", "all"); Comparator<Product> dynamicComparator = createDynamicComparator(userPreferences); products.sort(dynamicComparator); System.out.println("Dynamic sorting based on user preferences:"); products.forEach(System.out::println); } private static Comparator<Product> createDynamicComparator(Map<String, String> preferences) { String sortBy = preferences.getOrDefault("sortBy", "price"); String order = preferences.getOrDefault("order", "asc"); Comparator<Product> comparator; switch (sortBy.toLowerCase()) { case "price": comparator = Comparator.comparing(Product::getPrice); break; case "rating": comparator = Comparator.comparing(Product::getRating); break; case "sales": comparator = Comparator.comparing(Product::getSales); break; case "name": comparator = Comparator.comparing(Product::getName); break; default: comparator = Comparator.comparing(Product::getPrice); } if ("desc".equalsIgnoreCase(order)) { comparator = comparator.reversed(); } // Add secondary sorting comparator = comparator.thenComparing(Product::getName); return comparator; } } Summary
Key Points:
- Comparator vs Comparable:
Comparable: Natural ordering (modifies class)Comparator: Custom ordering (external to class)
- Factory Methods (Java 8+):
Comparator.comparing()- Create comparator from field extractorComparator.naturalOrder()- Natural orderingComparator.reverseOrder()- Reverse natural orderingComparator.nullsFirst()/nullsLast()- Handle null values
- Chaining:
thenComparing()- Combine multiple comparators- Build complex sorting logic step by step
- Common Use Cases:
- Multiple field sorting
- Custom ordering logic
- Null-safe sorting
- Dynamic sorting based on user preferences
Best Practices:
- Use method references for cleaner code:
Product::getName - Handle null values explicitly with
nullsFirst()ornullsLast() - Chain comparators for multi-field sorting
- Create reusable comparator instances as constants
- Use Comparator.comparing() instead of manual implementations
Performance Considerations:
- Comparator creation is generally cheap
- Complex chaining may have minor performance impact
- Consider caching frequently used comparators
- Prefer built-in methods over custom implementations for common cases
The Comparator interface provides powerful and flexible ways to define custom sorting logic in Java, making it essential for applications that require sophisticated data ordering.