Java Advanced Sorting

Custom sorting with Comparator and advanced techniques

🔄 What is Advanced Sorting?

Advanced sorting involves custom comparison logic using Comparator interface, lambda expressions, and method references. It enables complex sorting scenarios beyond natural ordering.


// Simple custom sorting
List<String> names = Arrays.asList("John", "Alice", "Bob");
names.sort((a, b) -> a.length() - b.length()); // Sort by length
System.out.println(names); // [Bob, John, Alice]
                                    

Output:

[Bob, John, Alice]

Sorted by string length instead of alphabetical order

Sorting Techniques

⚖️

Comparator

Custom comparison logic

Comparator.comparing(Person::getName)
🔗

Method Reference

Concise comparator syntax

String::compareToIgnoreCase
📊

Multiple Criteria

Chain multiple comparisons

comparing(Person::getAge)
.thenComparing(Person::getName)
🔄

Reverse Order

Descending sort

Comparator.reverseOrder()

🔹 Comparator Interface

Using Comparator for custom sorting logic:

import java.util.*;
import java.util.stream.Collectors;

class Person {
    private String name;
    private int age;
    private double salary;
    
    public Person(String name, int age, double salary) {
        this.name = name;
        this.age = age;
        this.salary = salary;
    }
    
    // Getters
    public String getName() { return name; }
    public int getAge() { return age; }
    public double getSalary() { return salary; }
    
    @Override
    public String toString() {
        return String.format("%s (age: %d, salary: %.0f)", name, age, salary);
    }
}

public class ComparatorExample {
    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person("Alice", 30, 50000),
            new Person("Bob", 25, 60000),
            new Person("Charlie", 35, 45000),
            new Person("David", 25, 55000)
        );
        
        // Sort by age (ascending)
        List<Person> byAge = people.stream()
            .sorted(Comparator.comparing(Person::getAge))
            .collect(Collectors.toList());
        System.out.println("By Age: " + byAge);
        
        // Sort by salary (descending)
        List<Person> bySalary = people.stream()
            .sorted(Comparator.comparing(Person::getSalary).reversed())
            .collect(Collectors.toList());
        System.out.println("By Salary (desc): " + bySalary);
        
        // Sort by name length
        List<Person> byNameLength = people.stream()
            .sorted(Comparator.comparing(p -> p.getName().length()))
            .collect(Collectors.toList());
        System.out.println("By Name Length: " + byNameLength);
    }
}

Output:

By Age: [Bob (age: 25, salary: 60000), David (age: 25, salary: 55000), Alice (age: 30, salary: 50000), Charlie (age: 35, salary: 45000)]

By Salary (desc): [Bob (age: 25, salary: 60000), David (age: 25, salary: 55000), Alice (age: 30, salary: 50000), Charlie (age: 35, salary: 45000)]

By Name Length: [Bob, Alice, David, Charlie]

🔹 Multiple Criteria Sorting

Sorting by multiple fields using thenComparing:

import java.util.*;

public class MultiCriteriaSorting {
    public static void main(String[] args) {
        List<Person> employees = Arrays.asList(
            new Person("Alice", 30, 50000),
            new Person("Bob", 25, 60000),
            new Person("Charlie", 25, 45000),
            new Person("David", 30, 55000),
            new Person("Eve", 25, 60000)
        );
        
        System.out.println("Original list:");
        employees.forEach(System.out::println);
        
        // Sort by age first, then by salary (descending), then by name
        List<Person> sorted = employees.stream()
            .sorted(Comparator.comparing(Person::getAge)
                    .thenComparing(Person::getSalary, Comparator.reverseOrder())
                    .thenComparing(Person::getName))
            .collect(Collectors.toList());
        
        System.out.println("\nSorted by age, then salary (desc), then name:");
        sorted.forEach(System.out::println);
        
        // Custom comparator with lambda
        employees.sort((p1, p2) -> {
            int ageCompare = Integer.compare(p1.getAge(), p2.getAge());
            if (ageCompare != 0) return ageCompare;
            
            int salaryCompare = Double.compare(p2.getSalary(), p1.getSalary()); // Reverse
            if (salaryCompare != 0) return salaryCompare;
            
            return p1.getName().compareTo(p2.getName());
        });
        
        System.out.println("\nUsing custom lambda comparator:");
        employees.forEach(System.out::println);
    }
}

Output:

Original list: Alice (30, 50000), Bob (25, 60000), Charlie (25, 45000), David (30, 55000), Eve (25, 60000)

Sorted: Bob (25, 60000), Eve (25, 60000), Charlie (25, 45000), David (30, 55000), Alice (30, 50000)

🔹 Advanced Sorting Techniques

Specialized sorting scenarios and optimizations:

import java.util.*;
import java.util.function.Function;

public class AdvancedSortingTechniques {
    
    // Custom sorting with null handling
    public static void nullSafeSorting() {
        List<String> words = Arrays.asList("apple", null, "banana", "cherry", null, "date");
        
        // Nulls first, then alphabetical
        words.sort(Comparator.nullsFirst(String::compareTo));
        System.out.println("Nulls first: " + words);
        
        // Nulls last, then alphabetical
        words.sort(Comparator.nullsLast(String::compareTo));
        System.out.println("Nulls last: " + words);
    }
    
    // Case-insensitive sorting
    public static void caseInsensitiveSorting() {
        List<String> names = Arrays.asList("alice", "BOB", "Charlie", "david");
        
        names.sort(String.CASE_INSENSITIVE_ORDER);
        System.out.println("Case insensitive: " + names);
        
        // Using method reference
        names.sort(String::compareToIgnoreCase);
        System.out.println("Method reference: " + names);
    }
    
    // Sorting with custom key extraction
    public static void keyExtractionSorting() {
        List<String> sentences = Arrays.asList(
            "The quick brown fox",
            "jumps over the lazy dog",
            "Hello world",
            "Java programming is fun"
        );
        
        // Sort by word count
        sentences.sort(Comparator.comparing(s -> s.split(" ").length));
        System.out.println("By word count: " + sentences);
        
        // Sort by last word
        sentences.sort(Comparator.comparing(s -> {
            String[] words = s.split(" ");
            return words[words.length - 1];
        }));
        System.out.println("By last word: " + sentences);
    }
    
    // Stable sorting demonstration
    public static void stableSorting() {
        List<Person> people = Arrays.asList(
            new Person("Alice", 25, 50000),
            new Person("Bob", 25, 60000),
            new Person("Charlie", 30, 45000),
            new Person("David", 25, 55000)
        );
        
        System.out.println("Original order:");
        people.forEach(System.out::println);
        
        // Stable sort by age (maintains relative order for equal elements)
        people.sort(Comparator.comparing(Person::getAge));
        System.out.println("Stable sort by age:");
        people.forEach(System.out::println);
    }
    
    public static void main(String[] args) {
        System.out.println("=== Null Safe Sorting ===");
        nullSafeSorting();
        
        System.out.println("\n=== Case Insensitive Sorting ===");
        caseInsensitiveSorting();
        
        System.out.println("\n=== Key Extraction Sorting ===");
        keyExtractionSorting();
        
        System.out.println("\n=== Stable Sorting ===");
        stableSorting();
    }
}

Output:

Nulls first: [null, null, apple, banana, cherry, date]

Case insensitive: [alice, BOB, Charlie, david]

By word count: [Hello world, The quick brown fox, Java programming is fun, jumps over the lazy dog]

Stable sort maintains original order for equal age values

🧠 Test Your Knowledge

What does Comparator.comparing(Person::getName).thenComparing(Person::getAge) do?