Когда использовать Comparable и Comparator
У меня есть список объектов, которые мне нужно отсортировать по полю, скажем, Оценка. Не задумываясь, я написал новый класс, который реализует Comparator, который выполняет задачу, и она работает.
Теперь, оглядываясь назад на это, я задаюсь вопросом, должен ли я вместо этого иметь свой класс, реализующий Comparable, вместо того, чтобы создавать новый класс, который реализует Comparator. Счет - это единственное поле, в котором объекты будут упорядочены.
Что я сделал приемлемым в качестве практики?
Является ли правильный подход "сначала нужно, чтобы класс реализовал Comparable (для естественного упорядочения), и если требуется альтернативное сравнение полей, то создайте новый класс, который реализует Comparator"?
Если (2) выше верно, то означает ли это, что следует реализовывать Comparator только после того, как класс реализует Comparable? (Предполагая, что я владею оригинальным классом).
19 ответов
Я бы сказал, что объект должен реализовывать Comparable, если это очевидный естественный способ сортировки класса, и любой, кому потребуется сортировка класса, обычно хотел бы сделать это таким образом.
Если, однако, сортировка была необычным использованием класса или сортировка имеет смысл только для конкретного варианта использования, тогда Comparator является лучшим вариантом.
Другими словами, учитывая имя класса, понятно, как будет сортироваться сопоставимый объект, или вам приходится прибегать к чтению javadoc? Если это последнее, то шансы на то, что каждый будущий случай использования сортировки потребует компаратора, в этом случае реализация сопоставимого может замедлить пользователей класса, а не ускорить их.
Использование Comparable
если вы хотите определить стандартное (естественное) упорядочение поведения рассматриваемого объекта, обычной практикой является использование для этого технического или естественного (базы данных?) идентификатора объекта.
использование Comparator
если вы хотите определить внешнее управляемое поведение упорядочения, это может переопределить поведение упорядочения по умолчанию.
Использование Comparable
:
- если объект находится под вашим контролем.
- если сравнивающее поведение является основным сравнивающим поведением.
использование Comparator
:
- если объект находится вне вашего контроля, и вы не можете заставить их реализовать
Comparable
, - когда вы хотите сравнить поведение, отличное от поведения по умолчанию (которое определяется
Comparable
) поведение.
Сопоставимый - java.lang.Comparable: int compareTo(Object o1)
Сопоставимый объект способен сравнивать себя с другим объектом. Сам класс должен реализовывать интерфейс java.lang.Comparable, чтобы иметь возможность сравнивать его экземпляры.
- Способен сравнивать текущий объект с предоставленным объектом.
- Используя это, мы можем реализовать
only one sort sequence
основанный на свойствах экземпляров. EX:Person.id
- В некоторых предопределенных классах, таких как String, Wrapper, Date, Calendar, реализован Comparable интерфейс.
Компаратор - java.util.Comparator: int compare(Object o1, Object o2)
Объект сравнения способен сравнивать два разных объекта. Класс сравнивает не свои экземпляры, а экземпляры некоторых других классов. Этот класс компаратора должен реализовывать интерфейс java.util.Comparator.
- Способен сравнивать любые два объекта одного типа.
- Используя это, мы можем реализовать
many sort sequence
и назовите каждый, основываясь на свойствах экземпляров. EX:Person.id, Person.name, Person.age
- Мы можем реализовать интерфейс Comparator для наших предопределенных классов для индивидуальной сортировки.
Пример:
public class Employee implements Comparable<Employee> {
private int id;
private String name;
private int age;
private long salary;
// Many sort sequences can be created with different names.
public static Comparator<Employee> NameComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getName().compareTo(e2.getName());
}
};
public static Comparator<Employee> idComparator = new Comparator<Employee>() {
@Override
public int compare(Employee e1, Employee e2) {
return Integer.valueOf(e1.getId()).compareTo(Integer.valueOf(e2.getId()));
}
};
public Employee() { }
public Employee(int id, String name, int age, long salary){
this.id = id;
this.name = name;
this.age = age;
this.salary = salary;
}
// setters and getters.
// Only one sort sequence can be created with in the class.
@Override
public int compareTo(Employee e) {
//return Integer.valueOf(this.id).compareTo(Integer.valueOf(e.id));
//return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
if (this.id > e.id) {
return 1;
}else if(this.id < e.id){
return -1;
}else {
return Character.toString(this.name.charAt(0)).compareToIgnoreCase(Character.toString(e.name.charAt(0)));
}
}
public static void main(String[] args) {
Employee e1 = new Employee(5, "Yash", 22, 1000);
Employee e2 = new Employee(8, "Tharun", 24, 25000);
List<Employee> list = new ArrayList<Employee>();
list.add(e1);
list.add(e2);
Collections.sort(list); // call @compareTo(o1)
Collections.sort(list, Employee.nameComparator); // call @compare (o1,o2)
Collections.sort(list, Employee.idComparator); // call @compare (o1,o2)
}
}
- Для настраиваемой сортировки мы используем компаратор @compare(o1, o2), а для других сценариев - сопоставимый @compareTo(o1), без изменения кода, если мы хотим отсортировать более одного поля, тогда мы используем компаратор.
Для Java 8 Lambda: Comparator см. Мой пост.
Comparable следует использовать при сравнении экземпляров одного и того же класса.
Компаратор может использоваться для сравнения экземпляров разных классов.
Comparable реализуется классом, который должен определить естественный порядок для своих объектов. Вроде String реализует Comparable.
Если вам нужен другой порядок сортировки, он может реализовать компаратор и определить свой собственный способ сравнения двух экземпляров.
Если сортировка объектов должна основываться на естественном порядке, тогда используйте Comparable, тогда как, если сортировку нужно выполнять по атрибутам различных объектов, используйте Comparator в Java.
Основное различие между Comparable и Comparator:
+------------------------------------------------------------------------------------+
¦ Comparable ¦ Comparator ¦
¦-----------------------------------------+------------------------------------------¦
¦ java.lang.Comparable ¦ java.util.Comparator ¦
¦-----------------------------------------+------------------------------------------¦
¦ int objOne.compareTo(objTwo) ¦ int compareTo(objOne, objTwo) ¦
¦-----------------------------------------+------------------------------------------¦
¦ Negative, if objOne < objTwo ¦ Same as Comparable ¦
¦ Zero, if objOne == objTwo ¦ ¦
¦ Positive, if objOne > objTwo ¦ ¦
¦-----------------------------------------+------------------------------------------¦
¦ You must modify the clas whose ¦ You build a class separate from to sort. ¦
¦ instances you want to sort. ¦ the class whose instances you want ¦
¦-----------------------------------------+------------------------------------------¦
¦ Only one sort sequemce can be created ¦ Many sort sequences can be created ¦
¦-----------------------------------------+------------------------------------------¦
¦ Implemented frequently in the API by: ¦ Meant to be implemented to sort ¦
¦ String, Wrapper classes, Date, Calandar ¦ instances of third-party classes. ¦
+------------------------------------------------------------------------------------+
Comparator делает все, что делает сравнимый, плюс еще.
| | Comparable | Comparator ._______________________________________________________________________________ Is used to allow Collections.sort to work | yes | yes Can compare multiple fields | yes | yes Lives inside the class you’re comparing and serves | | as a “default” way to compare | yes | yes Can live outside the class you’re comparing | no | yes Can have multiple instances with different method names | no | yes Input arguments can be a list of | just Object| Any type Can use enums | no | yes
Я нашел лучший подход к использованию компараторов в качестве анонимных классов следующим образом:
private static void sortAccountsByPriority(List<AccountRecord> accounts) {
Collections.sort(accounts, new Comparator<AccountRecord>() {
@Override
public int compare(AccountRecord a1, AccountRecord a2) {
return a1.getRank().compareTo(a2.getRank());
}
});
}
Вы можете создать несколько версий таких методов прямо внутри класса, который вы планируете сортировать. Таким образом, вы можете иметь:
- sortAccountsByPriority
- sortAccountsByType
sortAccountsByPriorityAndType
так далее...
Теперь вы можете использовать эти методы сортировки где угодно и получить повторное использование кода. Это дает мне все, что было бы сравнимо, плюс больше... так что я не вижу никакой причины использовать сравнимые вообще.
Я бы сказал:
- если сравнение интуитивно понятно, то непременно реализуем Comparable
- если неясно, является ли ваше сравнение интуитивно понятным, используйте Comparator, поскольку он более явный и, следовательно, более понятный для бедной души, которая должна поддерживать код
- если возможно более одного интуитивного сравнения, я бы предпочел Comparator, возможно, построенный методом фабрики в классе для сравнения.
- если сравнение специального назначения, используйте Comparator
Следующие пункты помогут вам решить, в каких ситуациях следует использовать Comparable, а в каком Comparator:
1) Код Доступен
2) Критерии одиночной и множественной сортировки
3) Arays.sort() и Collection.sort ()
4) Как ключи в SortedMap и SortedSet
5) Больше Количество классов против гибкости
6) Межклассовые сравнения
7) Естественный порядок
Для более подробной статьи вы можете обратиться, когда использовать сопоставимые и когда использовать компаратор
Если вам нужна естественная сортировка заказов - сопоставимая с пользователем, если вам нужна сортировка пользовательских заказов - используйте компаратор
Пример:
Class Employee{
private int id;
private String name;
private String department;
}
Естественная сортировка по порядку будет основываться на идентификаторе, потому что она будет уникальной, а сортировка по индивидуальному порядку - по имени и отделу.
Refrences:
Когда класс должен быть Comparable и / или Comparator? http://javarevisited.blogspot.com/2011/06/comparator-and-comparable-in-java.html
- Если на момент написания класса у вас был только один вариант использования сортировки, используйте Comparable.
- Только когда у вас есть несколько стратегий сортировки, используйте Comparator.
Здесь был похожий вопрос: когда класс должен быть Comparable и / или Comparator?
Я бы сказал следующее: реализовать Comparable для чего-то вроде естественного упорядочения, например, на основе внутреннего идентификатора
Реализуйте Comparator, если у вас есть более сложный алгоритм сравнения, например, несколько полей и так далее.
Сопоставимые:
Всякий раз, когда мы хотим хранить только однородные элементы и требовать естественный порядок сортировки по умолчанию, мы можем перейти к реализации класса comparable
интерфейс.
Компаратор:
Всякий раз, когда мы хотим сохранить однородные и гетерогенные элементы и хотим отсортировать их в заданном по умолчанию порядке сортировки, мы можем перейти к comparator
интерфейс.
Моя потребность была отсортирована по дате.
Итак, я использовал Comparable, и он легко работал для меня.
public int compareTo(GoogleCalendarBean o) {
// TODO Auto-generated method stub
return eventdate.compareTo(o.getEventdate());
}
Одно ограничение с Comparable - то, что они не могут использоваться для Коллекций кроме Списка.
Меня попросили отсортировать определенный диапазон чисел в лучшее время, чем в одном из интервью. (Не используя сортировку отсчетов)
Реализация интерфейса Comparable поверх объекта позволяет неявным алгоритмам сортировки использовать переопределенный метод compareTo для упорядочивания элементов сортировки, и это будет линейное время.
- Компаратор по многим критериям сравнения
- Сопоставимо, когда у вас есть только один критерий.
Comparable - это естественный порядок сортировки по умолчанию, при условии, что числовые значения имеют возрастание, а строки - алфавитный порядок. например:
Treeset t=new Treeset();
t.add(2);
t.add(1);
System.out.println(t);//[1,2]
Comparator - это пользовательский порядок сортировки, реализованный в пользовательском классе myComparator путем переопределения метода сравнения, например:
Treeset t=new Treeset(new myComparator());
t.add(55);
t.add(56);
class myComparator implements Comparator{
public int compare(Object o1,Object o2){
//Descending Logic
}
}
System.out.println(t);//[56,55]
Если у вас есть класс, лучше идти с Comparable. Обычно Comparator используется, если у вас нет класса, но вы должны использовать его как TreeSet или TreeMap, потому что Comparator может быть передан в качестве параметра в конструкторе TreeSet или TreeMap. Вы можете увидеть, как использовать Comparator и Comparable в http://preciselyconcise.com/java/collections/g_comparator.php
Очень простой подход состоит в том, чтобы предположить, что рассматриваемый класс сущностей будет представлен в базе данных, а затем в таблице базы данных вам понадобится индекс, составленный из полей класса сущностей? Если ответ "да", то реализуйте сравнимый и используйте индексные поля для естественного порядка сортировки. Во всех остальных случаях используйте компаратор.
Моя аннотация lib для реализации Comparable
а также Comparator
:
public class Person implements Comparable<Person> {
private String firstName;
private String lastName;
private int age;
private char gentle;
@Override
@CompaProperties({ @CompaProperty(property = "lastName"),
@CompaProperty(property = "age", order = Order.DSC) })
public int compareTo(Person person) {
return Compamatic.doComparasion(this, person);
}
}
Нажмите на ссылку, чтобы увидеть больше примеров. http://code.google.com/p/compamatic/wiki/CompamaticByExamples