в чем компаратор лучше сопоставимого?

«Как вы будете сортировать коллекцию объектов сотрудников по идентификатору или имени». Для этого мы можем использовать два интерфейса, т. е. Comparator and Comparable. кажется, это один из распространенных вопросов на собеседовании.

Но я не вижу причин, по которым я должен использовать оба для сортировки объектов сотрудников

Я думал о том, что comparator делает, а Comparable не может. Я понимаю, что если объекты (переменные экземпляра, которые сравниваются) имеют естественный порядок, то comparable - правильный выбор. но если требуется пользовательский порядок (например, длина строки), то можно написать comparator. моя точка зрения здесь comparator нужна только клиенту, если он хочет отсортировать данные по каким-то другим критериям. Например, я бы реализовал Employee class для сортировки по id с помощью comparable interface. но если клиент хочет отсортировать объекты Employee по String(имя), он реализует comparator либо как конкретный класс, либо анонимно при сортировке. Есть ли что-то, что мне здесь не хватает?

Например, в следующем коде для объекта Person мой метод compareTo сравнивает возраст и сортирует его. В методе сравнения я использую длину строки (имя человека) для сортировки. Теоретически я мог бы выполнить и то, и другое в методе compareTo, как я реализовал ниже.

наконец, есть ли какие-либо дополнительные преимущества одного из следующих по сравнению с другими. Я реализовал компаратор двумя способами: 1. как статический метод, который закомментирован 2. как анонимный объект (?) в основном методе, который закомментирован 3. make новый класс, который реализует компаратор и вызывает экземпляр этого класса в collections.sort() - этого я здесь не делал

(The commented-out parts of the code works. They are just different implementations)

mport java.util.Collections;
import java.util.Comparator;
import java.util.*;

public class PersonComparator implements Comparable{
    private String name;
    private int age;

    public PersonComparator(String name, int age) {
        this.name = name;
        this.age = age;
    }
@Override
public String toString() {
    return "name=" + name + ", age=" + age;
}

/*@Override
public int compareTo(Object obj) {
    if (!(obj instanceof PersonComparator)) {
        throw new ClassCastException("Invalid object");
    }
    PersonComparator p2 = (PersonComparator)obj;
    return this.age-p2.age;
}*/

/*Alternative CompareTo that checks for both age and name*/
 public int compareTo(Object obj) {
    if (!(obj instanceof PersonComparator)) {
        throw new ClassCastException("Invalid object");
    }
    PersonComparator p2 = (PersonComparator)obj;
    if (this.age!=p2.age){
        return this.age-p2.age;
    }
    else {
    return (this.name.length()-p2.name.length());
}
} 


/*public static Comparator nameLengthComparator 
= new Comparator() {


    @Override
    public int compare(Object obj1, Object obj2) {
        if (!(obj1 instanceof PersonComparator) || !(obj2 instanceof PersonComparator)){
            throw new ClassCastException("Invalid object");
        }
        else {
            PersonComparator p1 = (PersonComparator)obj1;
            PersonComparator p2 = (PersonComparator)obj2;
            return p1.name.length()-p2.name.length();
        }
}
};*/

 public static void main(String[] args){
     PersonComparator p1 = new PersonComparator("Alexander", 45);
     PersonComparator p2 = new PersonComparator("Pat", 27);
     PersonComparator p3 = new PersonComparator("Zacky", 45);
     PersonComparator p4 = new PersonComparator("Rake", 34);

     List<PersonComparator> list = new ArrayList<PersonComparator>();
     list.add(p1);
     list.add(p2);
     list.add(p3);
     list.add(p4);

     System.out.println("Before sorting "+ list);
     Collections.sort(list);
     //System.out.println("After sorting by age "+ list);
     //System.out.println("Before sorting "+ list);
     //Collections.sort(list, nameLengthComparator);
     System.out.println("After sorting by name length "+ list);
     /*Collections.sort(list, new Comparator<PersonComparator>() {
         @Override
            public int compare(PersonComparator p1, PersonComparator p2) {
                    return p1.name.length()-p2.name.length();
                }
        }
    );*/
     System.out.println("After sorting by name length "+ list);
 }

}

Спасибо


person brain storm    schedule 31.08.2013    source источник
comment
Это то, что вы говорите... кстати, используйте Comparable<T> с дженериками для обеспечения безопасности типов, а не используйте Object   -  person nachokk    schedule 31.08.2013
comment
С помощью интерфейса Comparable можно определить единый порядок сортировки для объектов класса. Интерфейс компаратора используется для определения нескольких порядков сортировки для объектов класса.   -  person Sanchit    schedule 31.08.2013


Ответы (7)


Comparable интерфейс

Интерфейс Comparable определяет естественный порядок типов. Предположим, у вас есть список из String или Integer объектов; вы можете передать этот список

Collections.sort(list);

и у вас будет отсортированный список. Как? Поскольку String и Integer реализуют интерфейс Comparable, а реализации интерфейса Comparable обеспечивают естественный порядок. Это похоже на определение класса, говорящее: «Если вы найдете набор объектов моего типа, упорядочите их в соответствии со стратегией, которую я определил в методе compareTo».

Теперь, когда вы определяете свой собственный тип, вы можете определить естественный порядок объектов вашего класса, реализуя интерфейс Comparable. Дополнительную информацию об упорядочении объектов см. в документации по Java.

Comparator интерфейс

Интерфейс Comparator описывает, как определить пользовательские стратегии для упорядочения объектов. Предположим, у нас есть простой тип Person, как показано ниже:

public class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Теперь, реализовав интерфейс Comparator, вы можете написать различные стратегии для упорядочения экземпляров вашего типа Person. Например, рассмотрим две стратегии для упорядочения Person объектов, приведенные ниже:

class StrategyOne implements Comparator<Person> {

    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().length() - p2.getName().length();
    }

}

class StrategyTwo implements Comparator<Person> {

    @Override
    public int compare(Person p1, Person p2) {
        return p1.getName().compareTo(p2.getName());
    }

}

Здесь StrategyOne упорядочивает Person объекты на основе длины их имен, а StrategyTwo упорядочивает Person объекты на основе лексикографического порядка их имен.

Способы реализации Comparator

Как видите, конкретные классы стратегий не имеют состояния, поэтому все экземпляры функционально эквивалентны. Итак, нам просто нужен один экземпляр любого конкретного класса стратегии. Таким образом, это должен быть синглтон. Использование анонимных классов будет создавать новый экземпляр каждый раз при выполнении вызова. Рассмотрите возможность сохранения объекта в закрытом статическом конечном поле и его повторного использования с помощью статических фабричных методов для доступа к ним. Например, вы можете повторно использовать две приведенные выше конкретные стратегии, как показано ниже:

class Strategies {
    private static final Comparator<Person> PERSON_NAME_LENGTH_COMPARATOR = new StrategyOne();

    private static final Comparator<Person> PERSON_NAME_LEXICAL_COMPARATOR = new StrategyTwo();

    public static Comparator<Person> personNameLengthComparator(){
         return  PERSON_NAME_LENGTH_COMPARATOR;
    }


    public static Comparator<Person> personNameLexicalComparator(){
         return  PERSON_NAME_LEXICAL_COMPARATOR;
    }
}

Резюме

Подводя итог, можно сказать, что интерфейс Comparable используется для определения естественного упорядочения класса, а интерфейс Comparator используется для определения конкретных стратегий упорядочения объектов.

person Debojit Saikia    schedule 31.08.2013
comment
Подумайте о том, чтобы сохранить объект в частном статическом конечном поле и повторно использовать его, используя статические фабричные методы для доступа к ним — хороший момент, следуя рекомендациям effective java by Joshua Bloch - person brain storm; 31.08.2013
comment
что вы подразумеваете под Как видите, конкретные классы стратегий не имеют состояния, поэтому все экземпляры функционально эквивалентны. - person brain storm; 31.08.2013
comment
@user1988876 user1988876: Мы не поддерживаем какое-либо состояние в наших классах стратегии. Поэтому они не имеют состояния. Возвращаемое значение метода compare зависит от переданных ему значений аргументов, его работа не зависит от состояния объекта, так как у него его нет. Неважно, сколько объектов в нашем классов стратегий, которые мы создаем, метод compare всех из них будет возвращать одно и то же значение для одного и того же набора параметров. Следовательно, мы можем сказать, что классы без сохранения состояния всегда функционально эквивалентны. - person Debojit Saikia; 31.08.2013

В чем компаратор лучше сопоставимого?

Это не "превосходство". Просто два интерфейса делают (примерно) одно и то же по-разному. В случае Comparable логика заказа находится в заказываемом объекте. В случае Comparator логика находится в классе, отличном от объявляемых объектов.

Но я не вижу причин, по которым я должен использовать оба для сортировки объектов сотрудников

Единственный случай, когда имеет смысл использовать оба, — это если вам нужно иметь возможность сортировать объекты в разном порядке. Затем вы можете объявить соответствующие классы как реализующие Comparable для «естественного» порядка и использовать объекты Comparator для реализации других порядков.

Кстати, компаратор скорее всего не должен реализовывать Comparable, и наоборот.

Если компаратор реализует Comparable, это означает, что вы пытаетесь упорядочить экземпляры самого объекта компаратора...

Ваш класс PersonComparator назван неправильно. Это действительно должно называться Person.


Не могли бы вы уточнить в своем ответе одну вещь: у нас уже есть метод equals() из класса Object, тогда почему интерфейс Comparator снова облегчает метод equals()?

Ряд пунктов:

  • Кажется, вы все еще путаете назначение Comparable и Comparator. Метод equals для объекта Comparator сравнивает компаратор с другими компараторами!!

  • Метод equals сообщает вам, равны ли два объекта, а не то, какой из них стоит первым.

  • Причина, по которой Comparator переопределяет equals, заключается исключительно в том, что они могут четко документировать, что делает equals(Object), когда вы вызываете его для объекта Comparator. (Фактическое поведение полностью соответствует Object.equals(Object)... но они, очевидно, считали это необходимым, потому что программисты постоянно ошибались в семантике метода.)

person Stephen C    schedule 31.08.2013
comment
Не могли бы вы пояснить в своем ответе, что у нас уже есть метод equals() из класса Object, тогда почему интерфейс Comparator снова облегчает метод equals()? - person Sanchit; 31.08.2013
comment
+1 за утверждение Comparable: — логика упорядочения находится в самом упорядочиваемом объекте и в компараторе: — логика упорядочения находится в классе, отличном от объектов, подлежащих сортировке - person krohit; 05.05.2017

В чем компаратор лучше сопоставимого?

Я не скажу, что это лучше, но одним из преимуществ является то, что он позволяет нам писать несколько последовательностей сортировки. В случае Comparable вам нужно будет реализовать этот интерфейс вашим классом, который вы хотите отсортировать, и вы можете написать только одну последовательность сортировки.

С помощью Comparator вы можете создавать разные классы для последовательностей сортировки, и во время сортировки вы просто передаете экземпляр Comparator методу COllections.sort().

Рассмотрим класс Employee, который имеет поля id, firstName и lastName. Если вы реализуете Comparable, вы можете написать только одну логику сортировки в методе compareTo.

Если вы реализуете Comparator, вы можете создать отдельные последовательности сортировки, создав отдельные классы. например IdSorter, FirstNameSorter и LastNameSorter, что позволяет сортировать Employee несколькими способами.

Читать

Сортировка пользовательских объектов с помощью компаратора

person Prasad Kharkar    schedule 31.08.2013

Comparable позволяет сортировать элементы в коллекциях на основе только одного поля. Comparator обеспечивает гибкость для сравнения элементов на основе более чем одного поля.

Например.

class Person implements Comparable
{

int age;
String name;


Person(int age,String name)
{
   this.age=age;
   this.name=name;
}

public int compareTo(Object o1)   // Either you can compare according to age or name
{
    Person p = (Person)o1;
    if (this.age==p.age)
    return 0;
    else if (this.age>p.age)
    return 1;
    else
    return -1;
}


public int compareTo(Object o)    //Based on name comparision
{
     return (this.name.compareTo((Person)o).name));
}
public static void main (String args[])
{
     List<Person> list = new ArrayList<Person>();
     Person o = new Person(12,"Steve");
     Person o1 = new Person(13,"Jason");
     list.add(o);
     list.add(o1);
     Collections.sort(list);
    }
}

В случае Comparable выше вы можете сортировать элементы по возрасту или имени. Но в случае Comparator вы можете сортировать элементы на основе более чем одного поля.

class AgeComparison implements Comparator
{
   public int compare(Object o1,Object o2)
{
        Person s1 = (Person)o1;
        Person s2 =(Person)o2;
        if (s1.age==s2.age)
        return 0;
        if(s1.age>s2.age)
        return 1;
        else
        return -1;
}

class NameComparison implements Comparator
{
     public int compare(Object o1,Object o2)
    {
        Person s1 = (Person)o1;
        Person s2 =(Person)o2;
        return (s1.age.compareTo(s2.age));
    }

}

Чтобы использовать Comparator, вы должны передать список и экземпляр класса, который вы должны использовать.

Collections.sort(list,new NameComparison());
Collections.sort(list,new AgeComparison());

В двух словах, преимуществом Comparator является гибкость сортировки списка на основе более чем одного поля объекта.

person Malwaregeek    schedule 31.08.2013

В общем, используйте Comparable, когда порядок "очевиден". Например, для строк вы используете алфавитный порядок, для чисел вы используете числовой порядок. Обратите внимание, что объект Comparable может реализовать только один метод compareTo(), поэтому вы получаете только один вариант — «естественный», «очевидный». Преимущество заключается в том, что это просто, и клиентскому коду не нужно выполнять дополнительную работу для сравнения вещей.

Используйте компаратор, если порядок менее очевиден, или вы можете захотеть иметь несколько вариантов. Например, книга может быть отсортирована по названию, автору, ISBN и т. д. У вас может быть три разных компаратора для обработки этих трех случаев. Возможно, вы захотите отсортировать строки в каком-то необычном порядке, например. особый случай для иностранного языка, игнорируя заглавные буквы и т. д.

Кроме того, если сортируемые вами Объекты не реализуют Comparable, или вы смешиваете типы, которые не любят сравнивать друг с другом (в общем, этого следует избегать, но, возможно, вы хотите иметь возможность сравнивать Книги и Авторы в один список в каком-то особом случае) вам нужно использовать Компаратор.

person user949300    schedule 31.08.2013
comment
На самом деле, даже со строками порядок не обязательно очевиден. В зависимости от того, что представляют строки, существует по крайней мере четыре различных способа сортировки X4.9, X10.3 и X4.12. Или, если уж на то пошло, привет, привет и привет. - person supercat; 17.12.2013

Вот... Я уже много писал об этом пояснении с картинками и пояснениями.

Пожалуйста, найдите ссылку ниже:

Comparable и Comparator

Одна мысль, которую вы всегда можете помнить, и это то, что «они не могут использоваться взаимозаменяемо».

person dharam    schedule 31.08.2013

Если вы используете компаратор, вам просто нужно добавить один класс компаратора и передать его методу Collections.sort() вместе с объектом List без каких-либо других изменений в существующем коде.

но если вы реализуете сопоставимый интерфейс, вам придется изменить код всех классов модели/компонента, чтобы переопределить метод compareTo().

так что для компаратора Loose Coupling лучше.

person Shrikant Dande    schedule 24.10.2016