Java Collections sort()
Learn to use Collections.sort() method to sort a list of objects using some examples.
By default, the sort() method sorts a given list into ascending order (or natural order). We can use Collections.reverseOrder() method, which returns a Comparator, for reverse sorting.
1. Sorting in Natural Order and Reverse Order
Collections.sort(list); //Sorts in natural order Collections.sort(list, Collections.reverseOrder()); //Sorts in reverse order
- Above method sorts the specified list of items into their natural order.
- All items must implement the Comparable interface.
- All items must be mutually comparable and should not throw ClassCastException .
- This sort is guaranteed to be stable. It means that equal elements will not be reordered as a result of the sort.
- The specified list must be modifiable, but need not to be resizable.
- The sort() does not return any value.
1.1. Sorting an ArrayList of Strings
Java program to sort a list of strings lexicographically (in the dictionary order).
List names = Arrays.asList("Alex", "Charles", "Brian", "David"); //Prints - [Alex, Brian, Charles, David] Collections.sort(names); //Prints - [David, Charles, Brian, Alex] Collections.sort(names, Collections.reverseOrder());
1.2. Sorting ArrayList of Objects by Field
We may require to sort a list of custom objects which can have their own sorting logic. In this case, implement the Comparator interface in the custom class.
For example, the domain object Employee has default sorting on the name field. Checkout for comparison logic in compareTo() method.
public class Employee implements Comparable < private Integer id; private String name; private String email; private LocalDate dateOfBirth; //Getters and Setters @Override public int compareTo(Employee e) < return this.getName().compareTo(e.getName()); >>
Nest Java program sorts the list of Employee objects by their name;
ArrayList employees = methodReturnsUnsortedList(); //Narutal order sorting Collections.sort(employees); //Reverse sorting Collections.sort(employees, Collections.reverseOrder());
2. Custom Sorting using Comparators
The second parameter in sort() method takes an instance of Comparator .
We can implement any kind of comparison logic with the help of comparators and then we can use sort() method to sort the list based on the given custom logic.
Collections.sort(List, Comparator);
We can create a separate Comparator instances for each kind of sorting need, and then we can combine those instances to create group sorting effect.
For example, if we want to sort the Employee list on three fields – id, name, and age. In this case, we need to create 3 Comparator instances.
2.1. Creating Custom Comparator
This is general syntax to create a Comparator in Java. In this case, we are creating a Comparator which will sort the Employee list by id field.
Comparator compareById = new Comparator() < @Override public int compare(Employee o1, Employee o2) < return o1.getId().compareTo(o2.getId()); >>; Comparator compareByName = new Comparator() < @Override public int compare(Employee o1, Employee o2) < return o1.getName().compareTo(o2.getName()); >>;
We can use lambda expression for further shortening the syntax.
//Id Comparator Comparator compareById = (Employee o1, Employee o2) -> o1.getId().compareTo( o2.getId() ); //Name Comparator Comparator compareByName = (Employee o1, Employee o2) -> o1.getName().compareTo( o2.getName() );
2.2. Using Comparator for Sorting
ArrayList employees = getUnsortedEmployeeList(); Comparator compareById = (Employee o1, Employee o2) -> o1.getId().compareTo( o2.getId() ); Collections.sort(employees, compareById); Collections.sort(employees, compareById.reversed());
In the above code examples, we learned to sort an ArrayList in default order or reverse order.
We also learned to use the Comparators for implementing the custom sorting logic.
Сортировка списков в Java
Для возможности сортировки объектов в коллекциях наследниках List в Java существует статический метод класса java.util.Collections .
Это значит вы можете сортировать элементы таких классов как ArrayList, LinkedList, CopyOnWriteArrayList и других классов, имплементирующих интерфейс List .
В общем виде, если у вас есть список из строк:
то после сортировки получите в списке порядок:
Простое использование метода sort() #
Если у нас в списке находятся объекты классов, которые известно как сравнить, то достаточно просто вызвать метод sort() и передать туда список. Таким образом в списке элементы поменяют порядок и будут отсортированы в порядке возрастания
//создание списка на основе массива var stringList = Arrays.asList("z", "b", "c", "a", "k", "z"); System.out.println(stringList); //сортировка списка в порядке возрастания Collections.sort(stringList); System.out.println(stringList);
Так мы можем сортировать множество стандартных классов, таких как String, Integer, Double, Character и множество других.
Если более точно выразиться, то без дополнительных параметров возможно отсортировать список из любых элементов, классы которых имплементируют интерфейс сравнения Comparable .
Сортировка в обратном порядке #
Если мы хотим сортировать элементы в обратном порядке. То для этого передадим дополнительный аргумент в метод сортировки:
//создание списка на основе массива var stringList = Arrays.asList("z", "b", "c", "a", "k", "z"); System.out.println(stringList); //сортировка списка в обратном направлении Collections.sort(stringList, Collections.reverseOrder()); System.out.println(stringList);
Добавляем возможность сортировки своих классов #
Если стандартные классы уже готовы к сортировке, то если мы напишем свой класс, то Java не знает как есть сравнивать с объектами этого же класса.
Чтобы научить сравнивать объекты есть два варианта:
- создать класс на основе Comparator и там прописать правила сравнения в методе int compare(T o1, T o2) . Полученный объект из класса использовать всегда, когда нам надо сортировать объекты. Такой вариант отлично подходит, когда нам надо сортировать объекты по разным правилам и можем использовать нужный нам класс Comparator.
- добавить в класс (являющимся, элементом списка) имплементацию интерфейса Comparable и прописать правила сравнения в методе int compareTo(T o) . Тогда не потребуется указывать каждый раз компаратор, данное правило сравнение будет по-умолчанию для этого объекта.
Оба метода возвращают целое число, которое обычно интерпретируется так:
- число больше 0 -> объект с которым сравнивают больше текущего
- число равно 0 -> объекты одинаковые
- число меньше 0 -> объект с которым сравнивают меньше текущего
Создадим свой класс, например для студента:
class Student private final String name; private final double avgMark; public Student(String name, double avgMark) this.name = name; this.avgMark = avgMark; > @Override public String toString() return " + name + '\'' + ", m=" + avgMark + '>'; > >
Класс специально минимально простой: все параметры задаются в конструкторе, и используются значения только для печати данных при вызове toString, что поможет нам в визуализации результата.
Для начала, посмотрим, что будет если мы попробуем отсортировать список из студентов:
var ivan = new Student("Иван", 4.3); var olga = new Student("Ольга", 3.8); var eugene = new Student("Женя", 4.9); var studentList = Arrays.asList(ivan, olga, eugene); System.out.println(studentList); //сортировка списка Collections.sort(studentList); System.out.println(studentList);
Такой код не скомпилируется, так как метод sort() не просто ожидает список, но еще важно, чтобы элемент списка был наследником Comparable:
public static T extends Comparable super T>> void sort(ListT> list) list.sort(null); >
Использование Comparable #
Для создания возможности сортировки, нам необходимо научить сравнить объекты с другими такого-же типа. И такая реализация будет использоваться по-умолчанию при сравнении объектов одного класса.
Имплементируем Comparable интерфейс, и реализуем метод compareTo:
class Student implements ComparableStudent> private final String name; private final double avgMark; public Student(String name, double avgMark) this.name = name; this.avgMark = avgMark; > @Override public String toString() return " + name + '\'' + ", m=" + avgMark + '>'; > @Override public int compareTo(Student o) return name.compareTo(o.name); > >
Обратите внимание, внутри метод мы решили сравнить две строки, а так как у String есть реализация Comparable — мы можем ее использовать.
В данном коде опущены части, с проверкой на null объектов o и полей класса.
Давайте проверим, как это будет работать:
var ivan = new Student("Иван", 4.3); var olga = new Student("Ольга", 3.8); var eugene = new Student("Женя", 4.9); var studentList = Arrays.asList(ivan, olga, eugene); System.out.println(studentList); //сортировка списка Collections.sort(studentList); System.out.println(studentList);
Все отлично, список отсортирован по полю name .
Вы можете делать более сложные условия сравнения, только не забывайте учитывать требование для успешной сортировки — два объекта, сколько бы мы их не сравнивали — должны всегда давать одинаковый результат.
Использование Comparator #
А что если нам надо сортировать студентов не по имени, а по средней оценке? И при этом оставить возможность сортировать по имени, которое должна использоваться по умолчанию для создания различных документов.
Нам на помощь придет отдельный класс Comparator , которые хранит в себе логику сравнения объектов и при сортировке, мы можем использовать нужное правило, то есть нужный объект класса Comparator .
Для начала добавим в класс Student геттеры, так как нам уже необходимо использовать данные класса в классе компаратора.
class Student implements ComparableStudent> private final String name; private final double avgMark; public Student(String name, double avgMark) this.name = name; this.avgMark = avgMark; > @Override public String toString() return " + name + '\'' + ", m=" + avgMark + '>'; > @Override public int compareTo(Student o) return name.compareTo(o.name); > public String getName() return name; > public double getAvgMark() return avgMark; > >
и теперь создадим класс Comparator , тип для сравнения Student :
class ComparatorByAvgMark implements ComparatorStudent> @Override public int compare(Student o1, Student o2) return Double.compare(o1.getAvgMark(), o2.getAvgMark()); > >
Мы снова использовали готовый метод для сравнения стандартного класса Double , это помогает не выдумывать свои реализации, а использовать уже существующие.
Также снова опущены проверки на null объектов o1, o2.
Теперь можно использовать данный класс, и в этот раз нам пригодится перегруженный метод Collections.sort() , который принимает компаратор:
var ivan = new Student("Иван", 4.3); var olga = new Student("Ольга", 3.8); var eugene = new Student("Женя", 4.9); var studentList = Arrays.asList(ivan, olga, eugene); System.out.println(studentList); //сортировка списка c использованием компаратора Collections.sort(studentList, new ComparatorByAvgMark()); System.out.println(studentList);
И мы видим — сортировка по возрастанию средней оценки студента.
Хорошо, давайте сделаем обратную сортировку, высокие оценки должны быть в начале списка. Для этого нам потребуется изменить поведение компаратора, и для этого у компаратора есть метод reversed() :
Collections.sort(studentList, new ComparatorByAvgMark().reversed());
и в итоге получим нужный результат:
Но это еще не все что может компаратор, можно создавать цепочки. Например, сначала сортируем по оценкам, а если оценки одинаковые, то по имени.
Это можно реализовать не создавая отдельного класса, а воспользоваться функцией:
Collections.sort(studentList, new ComparatorByAvgMark().reversed() .thenComparing(Student::getName));
При такой сортировки, оценки будут в порядке убывания, а внутри одной средней оценки, студенты будут по имени в порядке возрастания.
Метод sort() у самого списка #
Кроме использования метода Collections.sort() , можно вызывать похожий метод у самого списка List.sort() . Метод принимает один аргумент — компаратор.
На примере списка студентов:
studentList.sort(new ComparatorByAvgMark());