Comparator Interface in Java

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:

  1. Comparator vs Comparable:
  • Comparable: Natural ordering (modifies class)
  • Comparator: Custom ordering (external to class)
  1. Factory Methods (Java 8+):
  • Comparator.comparing() - Create comparator from field extractor
  • Comparator.naturalOrder() - Natural ordering
  • Comparator.reverseOrder() - Reverse natural ordering
  • Comparator.nullsFirst()/nullsLast() - Handle null values
  1. Chaining:
  • thenComparing() - Combine multiple comparators
  • Build complex sorting logic step by step
  1. Common Use Cases:
  • Multiple field sorting
  • Custom ordering logic
  • Null-safe sorting
  • Dynamic sorting based on user preferences

Best Practices:

  1. Use method references for cleaner code: Product::getName
  2. Handle null values explicitly with nullsFirst() or nullsLast()
  3. Chain comparators for multi-field sorting
  4. Create reusable comparator instances as constants
  5. Use Comparator.comparing() instead of manual implementations

Performance Considerations:

  1. Comparator creation is generally cheap
  2. Complex chaining may have minor performance impact
  3. Consider caching frequently used comparators
  4. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper