Comparable и Comparator в Java очень полезны для сортировки коллекции объектов. Java предоставляет некоторые встроенные методы для сортировки массива примитивных типов, массива оберток примитивных типов или списка. Здесь мы сначала узнаем, как можно отсортировать массив/список примитивных типов и оберток примитивных типов, а затем будем использовать интерфейсы java.lang.Comparable и java.util.Comparator для сортировки массива/списка пользовательских классов. Давайте посмотрим, как мы можем сортировать массив/список примитивных типов или объектов с помощью простой программы.
package com.journaldev.sort;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class JavaObjectSorting {
/**
* This class shows how to sort primitive arrays,
* Wrapper classes Object Arrays
* @param args
*/
public static void main(String[] args) {
//сортировка массива примитивных типов, например, массива int
int[] intArr = {5,9,1,10};
Arrays.sort(intArr);
System.out.println(Arrays.toString(intArr));
//сортировка массива строк
String[] strArr = {"A", "C", "B", "Z", "E"};
Arrays.sort(strArr);
System.out.println(Arrays.toString(strArr));
//сортировка списка объектов оберток примитивных типов
List strList = new ArrayList();
strList.add("A");
strList.add("C");
strList.add("B");
strList.add("Z");
strList.add("E");
Collections.sort(strList);
for(String str: strList) System.out.print(" "+str);
}
}
Результат выполнения вышеуказанной программы:
[1, 5, 9, 10]
[A, B, C, E, Z]
A B C E Z
Теперь давайте попробуем отсортировать массив объектов.
package com.journaldev.sort;
public class Employee {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
//в этом методе переопределяется вывод информации о сотруднике в удобочитаемом формате
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
}
Вот код, который я использовал для сортировки массива объектов Employee.
//сортировка массива объектов
Employee[] empArr = new Employee[4];
empArr[0] = new Employee(10, "Mikey", 25, 10000);
empArr[1] = new Employee(20, "Arun", 29, 20000);
empArr[2] = new Employee(5, "Lisa", 35, 5000);
empArr[3] = new Employee(1, "Pankaj", 32, 50000);
//сортировка массива сотрудников с использованием интерфейса Comparable
Arrays.sort(empArr);
System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));
При попытке запустить это, возникает следующее исключение времени выполнения.
Exception in thread "main" java.lang.ClassCastException: com.journaldev.sort.Employee cannot be cast to java.lang.Comparable
at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:290)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:157)
at java.util.ComparableTimSort.sort(ComparableTimSort.java:146)
at java.util.Arrays.sort(Arrays.java:472)
at com.journaldev.sort.JavaSorting.main(JavaSorting.java:41)
Comparable и Comparator
Java предоставляет интерфейс Comparable, который должен быть реализован любым пользовательским классом, если мы хотим использовать методы сортировки Arrays или Collections. Интерфейс Comparable имеет метод compareTo(T obj), который используется методами сортировки; вы можете проверить любой оболочечный класс, String или класс Date, чтобы подтвердить это. Мы должны переопределить этот метод таким образом, чтобы он возвращал отрицательное целое число, ноль или положительное целое число, если “этот” объект меньше, равен или больше объекта, переданного в качестве аргумента. После реализации интерфейса Comparable в классе Employee, вот получившийся класс Employee.
package com.journaldev.sort;
import java.util.Comparator;
public class Employee implements Comparable {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public int compareTo(Employee emp) {
// давайте отсортируем сотрудников по id в порядке возрастания
// возвращает отрицательное целое число, ноль или положительное целое число как id этого сотрудника
// меньше, равно или больше указанного объекта.
return (this.id - emp.id);
}
@Override
// это необходимо для вывода информации о сотруднике в удобочитаемом формате
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
}
Теперь, когда мы выполняем вышеуказанный фрагмент для сортировки массивов сотрудников и выводим его, вот результат.
Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]
Как вы можете видеть, массив Employees отсортирован по id в порядке возрастания. Однако в большинстве реальных сценариев мы хотим сортировку на основе разных параметров. Например, как CEO, мне бы хотелось отсортировать сотрудников по зарплате, а HR – по возрасту. Это ситуация, когда нам нужно использовать интерфейс Java Comparator, потому что реализация метода Comparable.compareTo(Object o) может обеспечить сортировку по умолчанию, и мы не можем изменять ее динамически. В то время как с помощью Comparator мы можем определить несколько методов с разными способами сортировки, а затем выбрать метод сортировки в зависимости от наших требований.
Java Comparator
Интерфейс Comparator compare(Object o1, Object o2) должен быть реализован, принимающий два аргумента Object, и должен быть реализован таким образом, чтобы он возвращал отрицательное целое число, если первый аргумент меньше второго, и возвращал ноль, если они равны, и положительное целое число, если первый аргумент больше второго. Интерфейсы Comparable и Comparator используют Generics для проверки типов на этапе компиляции, узнайте больше о Java Generics. Вот как мы можем создать разные реализации Comparator в классе Employee.
/**
* Comparator to sort employees list or array in order of Salary
*/
public static Comparator<Employee> SalaryComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return (int) (e1.getSalary() - e2.getSalary());
}
};
/**
* Comparator to sort employees list or array in order of Age
*/
public static Comparator<Employee> AgeComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getAge() - e2.getAge();
}
};
/**
* Comparator to sort employees list or array in order of Name
*/
public static Comparator<Employee> NameComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getName().compareTo(e2.getName());
}
};
Все вышеупомянутые реализации интерфейса Comparator являются анонимными классами. Мы можем использовать эти компараторы для передачи аргумента в функцию сортировки классов Arrays и Collections.
//сортировка массива сотрудников с использованием Comparator по зарплате
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));
//сортировка массива сотрудников с использованием Comparator по возрасту
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));
//сортировка массива сотрудников с использованием Comparator по имени
Arrays.sort(empArr, Employee.NameComparator);
System.out.println("Employees list sorted by Name:\n"+Arrays.toString(empArr));
Вот результат выполнения вышеуказанного фрагмента кода:
Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]
Теперь мы знаем, что если мы хотим отсортировать массив или список объектов Java, нам нужно реализовать интерфейс java Comparable для обеспечения сортировки по умолчанию, и мы должны реализовать интерфейс java Comparator для обеспечения различных способов сортировки. Мы также можем создать отдельный класс, который реализует интерфейс Comparator
, а затем использовать его. Вот окончательные классы, объясняющие Comparable и Comparator в Java.
package com.journaldev.sort;
import java.util.Comparator;
public class Employee implements Comparable {
private int id;
private String name;
private int age;
private long salary;
public int getId() {
return id;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public long getSalary() {
return salary;
}
public Employee(int id, String name, int age, int salary) {
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
@Override
public int compareTo(Employee emp) {
//отсортируем сотрудника по идентификатору в порядке возрастания
//возвращает отрицательное целое число, ноль или положительное целое число, если этот идентификатор сотрудника
//меньше, равен или больше указанного объекта
return (this.id - emp.id);
}
@Override
//это необходимо для вывода информации о сотруднике в удобочитаемом формате
public String toString() {
return "[id=" + this.id + ", name=" + this.name + ", age=" + this.age + ", salary=" +
this.salary + "]";
}
/**
* Comparator to sort employees list or array in order of Salary
*/
public static Comparator SalaryComparator = new Comparator() {
@Override
public int compare(Employee e1, Employee e2) {
return (int) (e1.getSalary() - e2.getSalary());
}
};
/**
* Comparator to sort employees list or array in order of Age
*/
public static Comparator AgeComparator = new Comparator() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getAge() - e2.getAge();
}
};
/**
* Comparator to sort employees list or array in order of Name
*/
public static Comparator NameComparator = new Comparator() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getName().compareTo(e2.getName());
}
};
}
Вот отдельная реализация класса интерфейса Comparator, который сравнивает два объекта Employee сначала по их идентификатору, а затем по имени, если они одинаковы.
package com.journaldev.sort;
import java.util.Comparator;
public class EmployeeComparatorByIdAndName implements Comparator<Employee> {
@Override
public int compare(Employee o1, Employee o2) {
int flag = o1.getId() - o2.getId();
if(flag==0) flag = o1.getName().compareTo(o2.getName());
return flag;
}
}
Вот тестовый класс, где мы используем разные способы сортировки объектов в Java с помощью Comparable и Comparator.
package com.journaldev.sort;
import java.util.Arrays;
public class JavaObjectSorting {
/**
* This class shows how to sort custom objects array/list
* implementing Comparable and Comparator interfaces
* @param args
*/
public static void main(String[] args) {
//сортировка пользовательского массива объектов
Employee[] empArr = new Employee[4];
empArr[0] = new Employee(10, "Mikey", 25, 10000);
empArr[1] = new Employee(20, "Arun", 29, 20000);
empArr[2] = new Employee(5, "Lisa", 35, 5000);
empArr[3] = new Employee(1, "Pankaj", 32, 50000);
//сортировка массива сотрудников с использованием реализации интерфейса Comparable
Arrays.sort(empArr);
System.out.println("Default Sorting of Employees list:\n"+Arrays.toString(empArr));
//сортировка массива сотрудников с использованием Comparator по зарплате
Arrays.sort(empArr, Employee.SalaryComparator);
System.out.println("Employees list sorted by Salary:\n"+Arrays.toString(empArr));
//сортировка массива сотрудников с использованием Comparator по возрасту
Arrays.sort(empArr, Employee.AgeComparator);
System.out.println("Employees list sorted by Age:\n"+Arrays.toString(empArr));
//сортировка массива сотрудников с использованием Comparator по имени
Arrays.sort(empArr, Employee.NameComparator);
System.out.println("Employees list sorted by Name:\n"+Arrays.toString(empArr));
//Список сотрудников отсортирован по ID, а затем по имени с использованием класса Comparator
empArr[0] = new Employee(1, "Mikey", 25, 10000);
Arrays.sort(empArr, new EmployeeComparatorByIdAndName());
System.out.println("Employees list sorted by ID and Name:\n"+Arrays.toString(empArr));
}
}
Вот результат выполнения вышеуказанной программы:
Default Sorting of Employees list:
[[id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000]]
Employees list sorted by Salary:
[[id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by Age:
[[id=10, name=Mikey, age=25, salary=10000], [id=20, name=Arun, age=29, salary=20000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000]]
Employees list sorted by Name:
[[id=20, name=Arun, age=29, salary=20000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000]]
Employees list sorted by ID and Name:
[[id=1, name=Mikey, age=25, salary=10000], [id=1, name=Pankaj, age=32, salary=50000], [id=5, name=Lisa, age=35, salary=5000], [id=10, name=Mikey, age=25, salary=10000]]
Интерфейсы java.lang.Comparable и java.util.Comparator являются мощными средствами, которые можно использовать для сортировки объектов в Java.
Comparable против Comparator
- Интерфейс Comparable можно использовать для обеспечения однотипной сортировки, в то время как интерфейс Comparator используется для обеспечения различных способов сортировки.
- Для использования Comparable класс должен его реализовать, в то время как для использования Comparator нам не нужно вносить изменения в класс.
- Интерфейс Comparable находится в пакете
java.lang
, в то время как интерфейс Comparator находится в пакетеjava.util
. - Нам не нужно вносить изменений в код на стороне клиента для использования Comparable, методы
Arrays.sort()
илиCollection.sort()
автоматически используют методcompareTo()
класса. Для Comparator клиенту нужно предоставить класс Comparator для использования в методе compare().
Вы знаете, что метод Collections.sort(), принимающий аргумент Comparator, следует шаблону стратегии?
Вы можете проверить полный код и другие примеры на основе ядра Java в нашем репозитории GitHub.
Source:
https://www.digitalocean.com/community/tutorials/comparable-and-comparator-in-java-example