Когда я должен реализовать Comparator?

Итак, я изучаю Comparator и Comparable, и у меня есть следующая проблема. У меня есть класс:

public class PhoneBook implements Comparator<Name>{

    private SortedMap<Name, Integer> directory ;

    //class code constructor etc.

    //this is the method that the compiler wants if implementing Comparator
    @Override
    public int compare(Name o1, Name o2) {

        return o1.firstName.compareTo(o2.firstName);
      }
}

Другой класс, Name реализует Comparable, и имеет две строки first и last в конструкторе. Чего я не до конца понимаю, так это функции Comparator, я прочитал документацию по Java и знаю, что она используется для сортировки элементов по-разному, без изменения класса Name. В моем примере это также может разрешать нулевые значения в некоторых ситуациях, но это объявление в моем классе конструктор работает нормально, и мне вообще не нужно реализовывать интерфейс Comparator в классе PhoneBook:

Public PhoneBook(ArrayList<Name> names, ArrayList<Integer> phones) {
    this.directory = new TreeMap<Name, Integer>(new Comparator<Name>(){

        @Override
        public int compare(Name o1, Name o2) {

            return o1.firstName.compareTo(o2.firstName);
        }

     });
    //other constructor code to populate map
}

И достигает той функциональности, которой я хочу добиться, без необходимости реализации интерфейса Comparator с помощью класса PhoneBook. Мой вопрос: когда класс может захотеть реализовать интерфейс Comparator? Есть ли другой способ заставить карту использовать другой метод сортировки (чем тот, который предоставляется интерфейсом Comparable в классе Name), не передавая ему анонимный класс при инициализации? Прошу прощения, если этот вопрос недостаточно ясен или не подходит для данного сайта.

Редактировать: я понимаю аргумент Comparable против Comparator и когда их использовать. Мой вопрос больше о том, как использовать Comparator. Можете ли вы отсортировать карту, не передавая новый компаратор при инициализации? Когда для класса хорошая идея реализовать этот интерфейс?

1 ответ

Решение

Классы, которые реализуют Comparator не должен делать ничего другого.

Поскольку большинство таких классов используются только в одном месте, очень часто они реализуются без имени, т.е. как анонимный класс, как вы делали во втором примере.

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

Обратите внимание, что в Java 8+ намного проще использовать лямбда-выражение вместо анонимного класса (поскольку это логически то, чем становится лямбда-выражение), а также ссылку на метод для многократного сравнения.

// Using anonymous class (Java 1.2+)
this.directory = new TreeMap<Name, Integer>(new Comparator<Name>() {
    @Override
    public int compare(Name n1, Name n2) {
        return n1.getFirstName().compareTo(n2.getFirstName());
    }
});
// Reusable named class (Java 1.2+)
public final class FirstNameComparator implements Comparator<Name> {
    @Override
    public int compare(Name n1, Name n2) {
        return n1.getFirstName().compareTo(n2.getFirstName());
    }
}

// Then use it like this:
this.directory = new TreeMap<Name, Integer>(new FirstNameComparator());
// Using lambda expression (Java 8+)
this.directory = new TreeMap<Name, Integer>(
    (n1, n2) -> n1.getFirstName().compareTo(n2.getFirstName())
);
// Using method reference (Java 8+)
public class PhoneBook {
    public PhoneBook(ArrayList<Name> names, ArrayList<Integer> phones) {
        this.directory = new TreeMap<Name, Integer>(PhoneBook::compareFirstName);
        // other constructor code
    }
    private static int compareFirstName(Name n1, Name n2) { // public, if reusable
        return n1.getFirstName().compareTo(n2.getFirstName());
    }
    // other PhoneBook code
}
// Using Comparator helper (Java 8+)
this.directory = new TreeMap<Name, Integer>(Comparator.comparing(Name::getFirstName));
Другие вопросы по тегам