Сортировка ArrayList своих объектов по полю в Java
Использование Comparator для сортировки по полю объекта
Проблема в том, что Collections.sort() требует, чтобы элементы списка либо реализовывали интерфейс Comparable (тогда объекты "знают", как сравнивать себя друг с другом), либо вы передаёте дополнительный объект-компаратор, который задаёт правило сравнения. Для вашей задачи — сортировка по произвольному полю без изменения класса — лучше всего подходит второй подход с Comparator.
Решение 1: Анонимный класс Comparator (классический способ)
import java.util.*;
public class Book {
String title;
int year;
public Book(String title, int year) {
this.title = title;
this.year = year;
}
public static void main(String[] args) {
ArrayList<Book> books = new ArrayList<>();
books.add(new Book("Война и мир", 1869));
books.add(new Book("1984", 1949));
books.add(new Book("Мастер и Маргарита", 1967));
// Сортировка по году (от старого к новому)
Collections.sort(books, new Comparator<Book>() {
@Override
public int compare(Book b1, Book b2) {
return Integer.compare(b1.year, b2.year);
}
});
// Вывод результата
for (Book b : books) {
System.out.println(b.year + " - " + b.title);
}
}
}
Как это работает: Метод compare() возвращает:
- Отрицательное число, если
b1.year < b2.year(b1 "меньше" b2) - Ноль, если равны
- Положительное число, если
b1.year > b2.year
Для сортировки по убыванию (от новых к старым) достаточно поменять порядок: Integer.compare(b2.year, b1.year).
Решение 2: Лямбда-выражения (Java 8+)
Современный и лаконичный способ:
Collections.sort(books, (b1, b2) -> Integer.compare(b1.year, b2.year));
Или ещё короче с использованием метода Comparator.comparingInt():
Collections.sort(books, Comparator.comparingInt(b -> b.year));
Для сортировки по строковому полю (например, по названию) используйте Comparator.comparing(b -> b.title).
Решение 3: Реализация Comparable (если сортировка по умолчанию одна)
Если ваш класс всегда должен сортироваться по одному полю (например, по году), можно реализовать интерфейс Comparable прямо в классе:
public class Book implements Comparable<Book> {
String title;
int year;
// ... конструктор, геттеры ...
@Override
public int compareTo(Book other) {
return Integer.compare(this.year, other.year);
}
// Теперь можно вызывать Collections.sort(books) без компаратора
}
Но этот подход менее гибкий: если понадобится сортировать по другому полю, придётся менять класс или использовать Comparator дополнительно.
Сортировка списка в обратном порядке
// Через компаратор
Collections.sort(books, Comparator.comparingInt(b -> b.year).reversed());
// Через Collections.reverseOrder()
Collections.sort(books, Collections.reverseOrder(Comparator.comparingInt(b -> b.year)));
Важно: Если ваше поле может быть null (например, String title может быть null), используйте Comparator.nullsLast() или nullsFirst() для избежания NullPointerException.
Сортировка с помощью Stream API (Java 8+)
Вместо Collections.sort(), который изменяет исходный список, можно создать отсортированную копию с помощью стримов. Это полезно, если вы не хотите мутировать исходные данные.
import java.util.*;
import java.util.stream.Collectors;
// ... класс Book остаётся без изменений ...
List<Book> sortedBooks = books.stream()
.sorted(Comparator.comparingInt(b -> b.year))
.collect(Collectors.toList());
Для сортировки по убыванию используйте .sorted(Comparator.comparingInt(b -> b.year).reversed()).
Сортировка по нескольким полям
Если нужно отсортировать сначала по году, а затем по названию (для книг одного года):
Collections.sort(books, Comparator
.comparingInt((Book b) -> b.year)
.thenComparing(b -> b.title));
Этот подход цепочки компараторов очень нагляден и часто используется в реальных проектах. Обратите внимание на явное указание типа (Book b) в первом компараторе — это необходимо для вывода типов в Java.
Дополнительный совет: Для полей примитивных типов (int, long, double) всегда используйте специализированные методы comparingInt(), comparingLong(), comparingDouble() — они избегают автоупаковки и работают быстрее.