1. Introduction to Collections Sorting
What is Collections Sorting?
Collections Sorting refers to the process of arranging elements in a collection in a specific order (ascending, descending, or custom order). Java provides powerful sorting capabilities through the Collections class and Comparator/Comparable interfaces.
Why Sorting is Important?
- Data organization and retrieval
- Improved search performance (binary search)
- Better user experience in UI displays
- Data analysis and reporting
- Algorithm requirements
Main Sorting Approaches:
- Natural Order (Comparable interface)
- Custom Order (Comparator interface)
- Multiple Criteria (chaining comparators)
- Java 8+ Features (lambda expressions, method references)
2. Core Sorting Interfaces and Classes
| Interface/Class | Purpose | Key Methods |
|---|---|---|
Comparable<T> | Natural ordering | compareTo(T o) |
Comparator<T> | Custom ordering | compare(T o1, T o2) |
Collections | Utility class | sort(), reverseOrder() |
List | Ordered collection | sort(Comparator) |
3. Complete Code Examples
Example 1: Natural Ordering with Comparable
import java.util.*;
// Class implementing Comparable for natural ordering
class Student implements Comparable<Student> {
private String name;
private int age;
private double grade;
public Student(String name, int age, double grade) {
this.name = name;
this.age = age;
this.grade = grade;
}
// Natural ordering by name
@Override
public int compareTo(Student other) {
return this.name.compareTo(other.name);
}
// Getters
public String getName() { return name; }
public int getAge() { return age; }
public double getGrade() { return grade; }
@Override
public String toString() {
return String.format("Student{name='%s', age=%d, grade=%.2f}", name, age, grade);
}
}
public class ComparableExample {
public static void main(String[] args) {
System.out.println("=== Natural Ordering with Comparable ===");
List<Student> students = new ArrayList<>();
students.add(new Student("John", 20, 85.5));
students.add(new Student("Alice", 22, 92.0));
students.add(new Student("Bob", 19, 78.5));
students.add(new Student("Diana", 21, 88.0));
System.out.println("Before sorting:");
students.forEach(System.out::println);
// Natural sorting (by name)
Collections.sort(students);
System.out.println("\nAfter natural sorting (by name):");
students.forEach(System.out::println);
// Using List.sort() method (Java 8+)
List<Student> students2 = new ArrayList<>(students);
students2.sort(null); // null means natural order
System.out.println("\nUsing List.sort() with natural order:");
students2.forEach(System.out::println);
}
}
Example 2: Custom Sorting with Comparator
import java.util.*;
class Employee {
private String name;
private String department;
private double salary;
private int yearsOfExperience;
public Employee(String name, String department, double salary, int yearsOfExperience) {
this.name = name;
this.department = department;
this.salary = salary;
this.yearsOfExperience = yearsOfExperience;
}
// Getters
public String getName() { return name; }
public String getDepartment() { return department; }
public double getSalary() { return salary; }
public int getYearsOfExperience() { return yearsOfExperience; }
@Override
public String toString() {
return String.format("Employee{name='%s', dept='%s', salary=%.2f, exp=%d}",
name, department, salary, yearsOfExperience);
}
}
public class ComparatorExample {
public static void main(String[] args) {
System.out.println("=== Custom Sorting with Comparator ===");
List<Employee> employees = new ArrayList<>();
employees.add(new Employee("John", "IT", 75000, 3));
employees.add(new Employee("Alice", "HR", 65000, 5));
employees.add(new Employee("Bob", "IT", 80000, 7));
employees.add(new Employee("Diana", "Finance", 90000, 4));
employees.add(new Employee("Mike", "IT", 70000, 2));
System.out.println("Original list:");
employees.forEach(System.out::println);
// 1. Sort by salary (ascending)
Comparator<Employee> salaryComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return Double.compare(e1.getSalary(), e2.getSalary());
}
};
Collections.sort(employees, salaryComparator);
System.out.println("\nSorted by salary (ascending):");
employees.forEach(System.out::println);
// 2. Sort by experience (descending)
Comparator<Employee> experienceComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return Integer.compare(e2.getYearsOfExperience(), e1.getYearsOfExperience());
}
};
Collections.sort(employees, experienceComparator);
System.out.println("\nSorted by experience (descending):");
employees.forEach(System.out::println);
// 3. Sort by department then by name
Comparator<Employee> deptThenNameComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
int deptCompare = e1.getDepartment().compareTo(e2.getDepartment());
if (deptCompare != 0) return deptCompare;
return e1.getName().compareTo(e2.getName());
}
};
Collections.sort(employees, deptThenNameComparator);
System.out.println("\nSorted by department then by name:");
employees.forEach(System.out::println);
// 4. Using Comparator static methods (Java 8+)
Comparator<Employee> naturalOrder = Comparator.comparing(Employee::getName);
employees.sort(naturalOrder);
System.out.println("\nSorted by name using Comparator.comparing():");
employees.forEach(System.out::println);
}
}
Example 3: Java 8+ Lambda and Method References
import java.util.*;
import java.util.stream.Collectors;
class Product {
private String name;
private String category;
private double price;
private int quantity;
public Product(String name, String category, double price, int quantity) {
this.name = name;
this.category = category;
this.price = price;
this.quantity = quantity;
}
// Getters
public String getName() { return name; }
public String getCategory() { return category; }
public double getPrice() { return price; }
public int getQuantity() { return quantity; }
@Override
public String toString() {
return String.format("Product{name='%s', category='%s', price=%.2f, quantity=%d}",
name, category, price, quantity);
}
}
public class Java8SortingExample {
public static void main(String[] args) {
System.out.println("=== Java 8+ Sorting Features ===");
List<Product> products = new ArrayList<>();
products.add(new Product("Laptop", "Electronics", 999.99, 10));
products.add(new Product("Mouse", "Electronics", 25.50, 50));
products.add(new Product("Desk", "Furniture", 299.99, 5));
products.add(new Product("Chair", "Furniture", 149.99, 15));
products.add(new Product("Book", "Education", 19.99, 100));
products.add(new Product("Pen", "Education", 2.99, 200));
System.out.println("Original list:");
products.forEach(System.out::println);
// 1. Using lambda expressions
System.out.println("\n1. Sort by price (ascending) - Lambda:");
products.sort((p1, p2) -> Double.compare(p1.getPrice(), p2.getPrice()));
products.forEach(System.out::println);
// 2. Using method references
System.out.println("\n2. Sort by name - Method reference:");
products.sort(Comparator.comparing(Product::getName));
products.forEach(System.out::println);
// 3. Sort by category then by price (descending)
System.out.println("\n3. Sort by category then by price (descending):");
products.sort(Comparator
.comparing(Product::getCategory)
.thenComparing(Comparator.comparing(Product::getPrice).reversed())
);
products.forEach(System.out::println);
// 4. Using nullsFirst and nullsLast
System.out.println("\n4. Handling null values:");
List<String> namesWithNulls = Arrays.asList("John", null, "Alice", "Bob", null, "Diana");
namesWithNulls.sort(Comparator.nullsFirst(String::compareTo));
System.out.println("With nulls first: " + namesWithNulls);
namesWithNulls.sort(Comparator.nullsLast(String::compareTo));
System.out.println("With nulls last: " + namesWithNulls);
// 5. Reverse order
System.out.println("\n5. Reverse order examples:");
List<Product> reverseByName = products.stream()
.sorted(Comparator.comparing(Product::getName).reversed())
.collect(Collectors.toList());
reverseByName.forEach(System.out::println);
// 6. Complex sorting with multiple criteria
System.out.println("\n6. Complex sorting - Category, then Price, then Quantity:");
products.sort(Comparator
.comparing(Product::getCategory)
.thenComparing(Product::getPrice)
.thenComparingInt(Product::getQuantity)
);
products.forEach(System.out::println);
}
}
Example 4: Multiple Field Sorting and Chaining
import java.util.*;
import static java.util.Comparator.*;
class Person {
private String firstName;
private String lastName;
private int age;
private String city;
public Person(String firstName, String lastName, int age, String city) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.city = city;
}
// Getters
public String getFirstName() { return firstName; }
public String getLastName() { return lastName; }
public int getAge() { return age; }
public String getCity() { return city; }
@Override
public String toString() {
return String.format("Person{%s %s, age=%d, city=%s}", firstName, lastName, age, city);
}
}
public class MultipleFieldSorting {
public static void main(String[] args) {
System.out.println("=== Multiple Field Sorting and Chaining ===");
List<Person> people = new ArrayList<>();
people.add(new Person("John", "Doe", 25, "New York"));
people.add(new Person("Alice", "Smith", 30, "Boston"));
people.add(new Person("Bob", "Johnson", 25, "Chicago"));
people.add(new Person("Alice", "Johnson", 28, "Chicago"));
people.add(new Person("John", "Smith", 35, "New York"));
people.add(new Person("Bob", "Smith", 25, "Boston"));
System.out.println("Original list:");
people.forEach(System.out::println);
// 1. Sort by first name then last name
System.out.println("\n1. Sort by first name then last name:");
people.sort(comparing(Person::getFirstName).thenComparing(Person::getLastName));
people.forEach(System.out::println);
// 2. Sort by city then age (descending)
System.out.println("\n2. Sort by city then age (descending):");
people.sort(comparing(Person::getCity).thenComparing(comparing(Person::getAge).reversed()));
people.forEach(System.out::println);
// 3. Sort by age then last name then first name
System.out.println("\n3. Sort by age then last name then first name:");
people.sort(comparingInt(Person::getAge)
.thenComparing(Person::getLastName)
.thenComparing(Person::getFirstName));
people.forEach(System.out::println);
// 4. Complex chaining with different orders
System.out.println("\n4. Complex: City (asc), Age (desc), LastName (asc), FirstName (asc):");
people.sort(comparing(Person::getCity)
.thenComparing(comparing(Person::getAge).reversed())
.thenComparing(Person::getLastName)
.thenComparing(Person::getFirstName));
people.forEach(System.out::println);
// 5. Using static import for cleaner code
System.out.println("\n5. Using static imports for cleaner syntax:");
List<Person> peopleCopy = new ArrayList<>(people);
peopleCopy.sort(comparing(Person::getCity)
.thenComparing(Person::getAge, reverseOrder())
.thenComparing(Person::getLastName)
.thenComparing(Person::getFirstName));
peopleCopy.forEach(System.out::println);
}
}
Example 5: Sorting Different Collection Types
import java.util.*;
import java.util.stream.Collectors;
public class DifferentCollectionsSorting {
public static void main(String[] args) {
System.out.println("=== Sorting Different Collection Types ===");
// 1. Sorting ArrayList
System.out.println("1. Sorting ArrayList:");
List<Integer> arrayList = new ArrayList<>(Arrays.asList(5, 2, 8, 1, 9, 3));
System.out.println("Before: " + arrayList);
Collections.sort(arrayList);
System.out.println("After: " + arrayList);
// 2. Sorting LinkedList
System.out.println("\n2. Sorting LinkedList:");
List<String> linkedList = new LinkedList<>(Arrays.asList("Orange", "Apple", "Banana", "Grape"));
System.out.println("Before: " + linkedList);
Collections.sort(linkedList, Comparator.reverseOrder());
System.out.println("After: " + linkedList);
// 3. Sorting HashSet (convert to List first)
System.out.println("\n3. Sorting HashSet:");
Set<Integer> hashSet = new HashSet<>(Arrays.asList(50, 20, 80, 10, 90, 30));
System.out.println("Before: " + hashSet);
List<Integer> sortedList = new ArrayList<>(hashSet);
Collections.sort(sortedList);
System.out.println("After: " + sortedList);
// 4. Sorting TreeSet (automatically sorted)
System.out.println("\n4. TreeSet (automatically sorted):");
Set<String> treeSet = new TreeSet<>(Arrays.asList("Zebra", "Apple", "Monkey", "Cat"));
System.out.println("TreeSet: " + treeSet);
// TreeSet with custom comparator
Set<String> reverseTreeSet = new TreeSet<>(Comparator.reverseOrder());
reverseTreeSet.addAll(Arrays.asList("Zebra", "Apple", "Monkey", "Cat"));
System.out.println("Reverse TreeSet: " + reverseTreeSet);
// 5. Sorting HashMap by keys
System.out.println("\n5. Sorting HashMap by keys:");
Map<Integer, String> hashMap = new HashMap<>();
hashMap.put(3, "Three");
hashMap.put(1, "One");
hashMap.put(4, "Four");
hashMap.put(2, "Two");
System.out.println("Before: " + hashMap);
Map<Integer, String> treeMap = new TreeMap<>(hashMap);
System.out.println("After (TreeMap): " + treeMap);
// 6. Sorting HashMap by values
System.out.println("\n6. Sorting HashMap by values:");
Map<String, Integer> scores = new HashMap<>();
scores.put("John", 85);
scores.put("Alice", 92);
scores.put("Bob", 78);
scores.put("Diana", 92);
System.out.println("Before: " + scores);
// Convert to List of entries and sort
List<Map.Entry<String, Integer>> entries = new ArrayList<>(scores.entrySet());
entries.sort(Map.Entry.comparingByValue(Comparator.reverseOrder()));
System.out.println("After sorting by value (descending):");
entries.forEach(entry -> System.out.println(entry.getKey() + ": " + entry.getValue()));
// 7. Using LinkedHashMap to preserve order
System.out.println("\n7. Preserving sort order with LinkedHashMap:");
Map<String, Integer> sortedMap = entries.stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(e1, e2) -> e1,
LinkedHashMap::new
));
System.out.println("Sorted LinkedHashMap: " + sortedMap);
// 8. Sorting arrays
System.out.println("\n8. Sorting arrays:");
int[] numbers = {5, 2, 8, 1, 9, 3};
System.out.println("Before: " + Arrays.toString(numbers));
Arrays.sort(numbers);
System.out.println("After: " + Arrays.toString(numbers));
// Sorting object arrays
String[] names = {"John", "Alice", "Bob", "Diana"};
System.out.println("Before: " + Arrays.toString(names));
Arrays.sort(names, Comparator.reverseOrder());
System.out.println("After: " + Arrays.toString(names));
}
}
Example 6: Real-World Application - Employee Management System
import java.util.*;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
class Employee {
private int id;
private String name;
private String department;
private double salary;
private LocalDate hireDate;
private int performanceRating;
public Employee(int id, String name, String department, double salary,
LocalDate hireDate, int performanceRating) {
this.id = id;
this.name = name;
this.department = department;
this.salary = salary;
this.hireDate = hireDate;
this.performanceRating = performanceRating;
}
// Getters
public int getId() { return id; }
public String getName() { return name; }
public String getDepartment() { return department; }
public double getSalary() { return salary; }
public LocalDate getHireDate() { return hireDate; }
public int getPerformanceRating() { return performanceRating; }
public long getYearsOfService() {
return ChronoUnit.YEARS.between(hireDate, LocalDate.now());
}
@Override
public String toString() {
return String.format("Employee{id=%d, name='%s', dept='%s', salary=%.2f, " +
"hireDate=%s, rating=%d, years=%d}",
id, name, department, salary, hireDate, performanceRating, getYearsOfService());
}
}
public class EmployeeManagementSystem {
public static void main(String[] args) {
System.out.println("=== Employee Management System - Sorting Examples ===");
List<Employee> employees = Arrays.asList(
new Employee(101, "John Smith", "Engineering", 75000,
LocalDate.of(2020, 3, 15), 4),
new Employee(102, "Alice Johnson", "Marketing", 65000,
LocalDate.of(2019, 6, 10), 5),
new Employee(103, "Bob Wilson", "Engineering", 80000,
LocalDate.of(2018, 1, 20), 3),
new Employee(104, "Diana Brown", "HR", 60000,
LocalDate.of(2021, 8, 5), 4),
new Employee(105, "Mike Davis", "Engineering", 90000,
LocalDate.of(2017, 11, 30), 5),
new Employee(106, "Sarah Miller", "Marketing", 70000,
LocalDate.of(2020, 2, 28), 4)
);
System.out.println("Original employee list:");
employees.forEach(System.out::println);
// 1. Sort by salary (descending)
System.out.println("\n1. Top earners (salary descending):");
employees.stream()
.sorted(Comparator.comparing(Employee::getSalary).reversed())
.limit(3)
.forEach(System.out::println);
// 2. Sort by years of service (descending)
System.out.println("\n2. Most experienced employees:");
employees.stream()
.sorted(Comparator.comparing(Employee::getYearsOfService).reversed())
.forEach(System.out::println);
// 3. Sort by department then by performance rating (descending) then by name
System.out.println("\n3. By department, performance (desc), then name:");
employees.stream()
.sorted(Comparator
.comparing(Employee::getDepartment)
.thenComparing(Comparator.comparing(Employee::getPerformanceRating).reversed())
.thenComparing(Employee::getName))
.forEach(System.out::println);
// 4. Group by department and sort employees within each group
System.out.println("\n4. Employees grouped by department (sorted by name):");
Map<String, List<Employee>> byDepartment = employees.stream()
.sorted(Comparator.comparing(Employee::getName))
.collect(Collectors.groupingBy(Employee::getDepartment));
byDepartment.forEach((dept, empList) -> {
System.out.println("\n" + dept + " Department:");
empList.forEach(emp -> System.out.println(" - " + emp.getName() +
" (Rating: " + emp.getPerformanceRating() + ")"));
});
// 5. Complex business logic: Bonus calculation and sorting
System.out.println("\n5. Bonus calculation and ranking:");
List<Employee> bonusRanking = employees.stream()
.sorted(Comparator
.comparing(Employee::getPerformanceRating).reversed()
.thenComparing(Employee::getYearsOfService).reversed()
.thenComparing(Employee::getSalary).reversed())
.toList();
System.out.println("Bonus ranking (Performance > Experience > Salary):");
for (int i = 0; i < bonusRanking.size(); i++) {
Employee emp = bonusRanking.get(i);
System.out.printf("%d. %s (Rating: %d, Exp: %d years, Salary: $%.2f)%n",
i + 1, emp.getName(), emp.getPerformanceRating(),
emp.getYearsOfService(), emp.getSalary());
}
// 6. Finding median salary by department
System.out.println("\n6. Department salary analysis:");
byDepartment.forEach((dept, empList) -> {
OptionalDouble avgSalary = empList.stream()
.mapToDouble(Employee::getSalary)
.average();
OptionalDouble medianSalary = empList.stream()
.mapToDouble(Employee::getSalary)
.sorted()
.skip(empList.size() / 2)
.findFirst();
System.out.printf("%s: Avg=%.2f, Median=%.2f, Count=%d%n",
dept, avgSalary.orElse(0), medianSalary.orElse(0), empList.size());
});
}
}
Example 7: Advanced Sorting Techniques
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
class AdvancedSortingTechniques {
// Utility method to remove duplicates by key
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Set<Object> seen = ConcurrentHashMap.newKeySet();
return t -> seen.add(keyExtractor.apply(t));
}
}
class Student {
private String name;
private int age;
private String grade;
private double gpa;
private String major;
public Student(String name, int age, String grade, double gpa, String major) {
this.name = name;
this.age = age;
this.grade = grade;
this.gpa = gpa;
this.major = major;
}
// Getters
public String getName() { return name; }
public int getAge() { return age; }
public String getGrade() { return grade; }
public double getGpa() { return gpa; }
public String getMajor() { return major; }
@Override
public String toString() {
return String.format("Student{name='%s', age=%d, grade='%s', gpa=%.2f, major='%s'}",
name, age, grade, gpa, major);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
}
public class AdvancedSortingExamples {
public static void main(String[] args) {
System.out.println("=== Advanced Sorting Techniques ===");
List<Student> students = Arrays.asList(
new Student("John", 20, "A", 3.8, "Computer Science"),
new Student("Alice", 22, "B", 3.5, "Mathematics"),
new Student("Bob", 21, "A", 3.9, "Computer Science"),
new Student("Diana", 20, "C", 3.2, "Physics"),
new Student("John", 23, "B", 3.6, "Engineering"), // Duplicate name
new Student("Mike", 19, "A", 4.0, "Computer Science"),
new Student("Alice", 21, "A", 3.7, "Biology") // Duplicate name
);
// 1. Remove duplicates and sort
System.out.println("1. Unique students sorted by name:");
students.stream()
.filter(AdvancedSortingTechniques.distinctByKey(Student::getName))
.sorted(Comparator.comparing(Student::getName))
.forEach(System.out::println);
// 2. Custom comparator with null safety
System.out.println("\n2. Sorting with null safety:");
List<String> namesWithNulls = Arrays.asList("John", null, "Alice", "Bob", null, "Diana");
namesWithNulls.sort(Comparator.nullsFirst(String.CASE_INSENSITIVE_ORDER));
System.out.println("Nulls first: " + namesWithNulls);
// 3. Sorting with custom order (enum-like)
System.out.println("\n3. Custom grade order (A > B > C):");
Map<String, Integer> gradeOrder = Map.of("A", 1, "B", 2, "C", 3);
students.stream()
.sorted(Comparator.comparing(s -> gradeOrder.get(s.getGrade())))
.forEach(System.out::println);
// 4. Parallel sorting for large datasets
System.out.println("\n4. Parallel sorting demonstration:");
List<Integer> largeList = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 20; i++) { // Using small size for demo
largeList.add(random.nextInt(1000));
}
System.out.println("Before: " + largeList);
List<Integer> parallelSorted = largeList.parallelStream()
.sorted()
.collect(Collectors.toList());
System.out.println("After: " + parallelSorted);
// 5. Stable vs unstable sorting demonstration
System.out.println("\n5. Stable sorting demonstration:");
List<Student> stableSort = students.stream()
.sorted(Comparator.comparing(Student::getGrade))
.sorted(Comparator.comparing(Student::getMajor))
.collect(Collectors.toList());
System.out.println("After stable sort (major then grade):");
stableSort.forEach(System.out::println);
// 6. Sorting with composed comparators
System.out.println("\n6. Composed comparator with different orders:");
Comparator<Student> complexComparator = Comparator
.comparing(Student::getMajor)
.thenComparing(Student::getGpa, Comparator.reverseOrder())
.thenComparing(Student::getAge)
.thenComparing(Student::getName);
students.stream()
.sorted(complexComparator)
.forEach(System.out::println);
// 7. Using thenComparing with different types
System.out.println("\n7. Mixed type comparisons:");
students.stream()
.sorted(Comparator
.comparing(Student::getMajor)
.thenComparingDouble(Student::getGpa)
.thenComparingInt(Student::getAge)
.thenComparing(Student::getName))
.forEach(System.out::println);
// 8. Case-insensitive sorting
System.out.println("\n8. Case-insensitive sorting:");
List<String> mixedCase = Arrays.asList("apple", "Banana", "CHERRY", "date", "Elderberry");
mixedCase.sort(String.CASE_INSENSITIVE_ORDER);
System.out.println("Case-insensitive: " + mixedCase);
// 9. Locale-sensitive sorting
System.out.println("\n9. Locale-sensitive sorting:");
List<String> words = Arrays.asList("café", "resume", "naïve", "façade");
words.sort(Comparator.naturalOrder());
System.out.println("Default locale: " + words);
// 10. Sorting with custom collation
System.out.println("\n10. Custom collation (by string length then alphabetically):");
List<String> customSort = Arrays.asList("apple", "kiwi", "banana", "fig", "cherry");
customSort.sort(Comparator
.comparing(String::length)
.thenComparing(Comparator.naturalOrder()));
System.out.println("Custom collation: " + customSort);
}
}
Example 8: Performance Comparison and Best Practices
import java.util.*;
import java.util.concurrent.TimeUnit;
public class SortingPerformance {
public static void main(String[] args) {
System.out.println("=== Sorting Performance Comparison ===");
// Generate test data
List<Integer> smallList = generateRandomList(1000);
List<Integer> mediumList = generateRandomList(10000);
List<Integer> largeList = generateRandomList(100000);
// 1. Compare Collections.sort() vs List.sort()
System.out.println("1. Collections.sort() vs List.sort()");
compareSortMethods(smallList, "Small list");
compareSortMethods(mediumList, "Medium list");
compareSortMethods(largeList, "Large list");
// 2. Compare different sorting algorithms (via different data patterns)
System.out.println("\n2. Different data patterns:");
testDifferentDataPatterns();
// 3. Memory usage comparison
System.out.println("\n3. Memory usage comparison:");
compareMemoryUsage();
// 4. Best practices demonstration
System.out.println("\n4. Best Practices:");
demonstrateBestPractices();
}
private static List<Integer> generateRandomList(int size) {
List<Integer> list = new ArrayList<>(size);
Random random = new Random();
for (int i = 0; i < size; i++) {
list.add(random.nextInt(size * 10));
}
return list;
}
private static void compareSortMethods(List<Integer> list, String label) {
List<Integer> copy1 = new ArrayList<>(list);
List<Integer> copy2 = new ArrayList<>(list);
long startTime = System.nanoTime();
Collections.sort(copy1);
long collectionsTime = System.nanoTime() - startTime;
startTime = System.nanoTime();
copy2.sort(null);
long listSortTime = System.nanoTime() - startTime;
System.out.printf("%s - Collections.sort: %d ms, List.sort: %d ms%n",
label,
TimeUnit.NANOSECONDS.toMicros(collectionsTime),
TimeUnit.NANOSECONDS.toMicros(listSortTime));
}
private static void testDifferentDataPatterns() {
// Already sorted
List<Integer> sorted = new ArrayList<>();
for (int i = 0; i < 10000; i++) sorted.add(i);
// Reverse sorted
List<Integer> reverseSorted = new ArrayList<>();
for (int i = 10000; i > 0; i--) reverseSorted.add(i);
// Random
List<Integer> random = generateRandomList(10000);
// Few unique
List<Integer> fewUnique = new ArrayList<>();
Random rand = new Random();
for (int i = 0; i < 10000; i++) fewUnique.add(rand.nextInt(10));
testSortingPerformance(sorted, "Already sorted");
testSortingPerformance(reverseSorted, "Reverse sorted");
testSortingPerformance(random, "Random");
testSortingPerformance(fewUnique, "Few unique values");
}
private static void testSortingPerformance(List<Integer> list, String label) {
List<Integer> copy = new ArrayList<>(list);
long startTime = System.nanoTime();
Collections.sort(copy);
long duration = System.nanoTime() - startTime;
System.out.printf("%s: %d ms%n", label, TimeUnit.NANOSECONDS.toMicros(duration));
}
private static void compareMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
// Before sorting
runtime.gc();
long memoryBefore = runtime.totalMemory() - runtime.freeMemory();
List<Integer> list = generateRandomList(100000);
runtime.gc();
long memoryAfterList = runtime.totalMemory() - runtime.freeMemory();
Collections.sort(list);
runtime.gc();
long memoryAfterSort = runtime.totalMemory() - runtime.freeMemory();
System.out.printf("Memory - List: %d KB, After sort: %d KB, Difference: %d KB%n",
(memoryAfterList - memoryBefore) / 1024,
(memoryAfterSort - memoryBefore) / 1024,
(memoryAfterSort - memoryAfterList) / 1024);
}
private static void demonstrateBestPractices() {
List<String> names = Arrays.asList("John", "Alice", "Bob", "Diana");
// GOOD: Use Comparator.comparing for readability
names.sort(Comparator.comparing(String::length));
System.out.println("By length: " + names);
// GOOD: Chain comparators for multiple criteria
names.sort(Comparator
.comparing(String::length)
.thenComparing(Comparator.naturalOrder()));
System.out.println("By length then alphabetically: " + names);
// GOOD: Use static imports for cleaner code
names.sort(Comparator
.comparing(String::length)
.thenComparing(naturalOrder()));
System.out.println("With static imports: " + names);
// GOOD: Handle null values explicitly
List<String> namesWithNulls = Arrays.asList("John", null, "Alice", null, "Bob");
namesWithNulls.sort(Comparator.nullsLast(naturalOrder()));
System.out.println("With nulls handled: " + namesWithNulls);
// BAD: Avoid complex anonymous comparators
// Instead of this:
// names.sort((s1, s2) -> {
// int lenCompare = Integer.compare(s1.length(), s2.length());
// if (lenCompare != 0) return lenCompare;
// return s1.compareTo(s2);
// });
// GOOD: Use this:
names.sort(Comparator
.comparing(String::length)
.thenComparing(naturalOrder()));
System.out.println("Clean comparator chaining: " + names);
}
}
9. Conclusion
Key Takeaways:
- Natural Ordering: Use
Comparableinterface for default sorting - Custom Ordering: Use
Comparatorinterface for flexible sorting - Java 8+ Features: Leverage lambdas, method references, and factory methods
- Multiple Criteria: Chain comparators using
thenComparing() - Performance: Choose the right algorithm and data structure
Best Practices:
- ✅ Use
Comparator.comparing()for readable code - ✅ Chain comparators for multiple sorting criteria
- ✅ Handle null values explicitly with
nullsFirst()/nullsLast() - ✅ Use static imports for cleaner comparator chains
- ✅ Prefer
List.sort()overCollections.sort()in Java 8+
Performance Tips:
- Pre-sort data when possible for better performance
- Use appropriate data structures (TreeSet, TreeMap for auto-sorting)
- Consider parallel streams for large datasets
- Avoid sorting if you only need a few top/bottom elements
Common Sorting Patterns:
// Simple ascending sort list.sort(Comparator.naturalOrder()); // Simple descending sort list.sort(Comparator.reverseOrder()); // Sort by property list.sort(Comparator.comparing(MyClass::getProperty)); // Sort by multiple properties list.sort(Comparator .comparing(MyClass::getProperty1) .thenComparing(MyClass::getProperty2)); // Sort with null handling list.sort(Comparator.nullsLast(Comparator.naturalOrder()));
Final Thoughts:
Java provides comprehensive and flexible sorting capabilities. The key is choosing the right approach for your specific use case:
- Simple sorts: Use natural ordering or basic comparators
- Complex sorts: Use comparator chaining
- Performance-critical: Consider algorithm choice and data structures
- Modern code: Leverage Java 8+ features for cleaner, more readable code
Mastering collections sorting will make you more effective at data manipulation and algorithm implementation in Java!