Почему Collections.sort не может использовать компаратор, а List.sort должен использовать компаратор? - PullRequest
5 голосов
/ 15 апреля 2020

Я не понимаю логику c, почему List.sort () не имеет версии без компаратора.

Специально я вижу, что можно вызвать List.sort с нулевым значением:
list.sort(null), и кажется, что он использует естественный порядок сортировки.

Я заметил, что в моей IDE Collections.sort () вызывает List.sort (null), поэтому мне было интересно, почему List.sort, который кажется чтобы быть недавно введенным в Java 8, не было простой версии без компаратора, которая во многих случаях не нужна.

Также я не уверен в этой ситуации, лучше ли вызывать List. sort (null), чтобы избежать лишнего вызова, или, если все еще предпочтительнее, вызвать Collections.sort () и избежать уродливого аргумента null.

Ответы [ 3 ]

7 голосов
/ 15 апреля 2020

tldr

Это дизайнерское решение, смешанное с некоторыми историческими причинами. Можно было бы добавить list.sort(), но это не могло обеспечить безопасность во время компиляции, только во время выполнения. Что было бы довольно странным и необычным дизайном для JDK.


Collections # sort

Коллекции, поскольку они могут указывать границы типов в объявлении метода, имеют возможность принудительно Comparable элементов:

public static <T extends Comparable<? super T>> void sort(List<T> list)

Таким образом, метод может гарантировать, что коллекция содержит Comparable элементов. Так что Comparator не требуется.


Список # сортировка

List не может этого сделать. Его обобщенный тип c должен разрешать все, вы должны иметь возможность использовать List<Dog>, хотя, возможно, это не Comparable.

Так что если будет list.sort(), то этот метод будет нужно выяснить, является ли список обобщенных c type T равным Comparable или нет. Но эта информация присутствует только во время выполнения, мы хотим, чтобы она была во время компиляции для хорошего дизайна.

Поскольку это может привести к плохому дизайну, list.sort() не существует. Следовательно, дополнительный метод форсирования Comparator, который может обеспечить безопасность во время компиляции ..


list.sort (null)

Наличие list.sort(null) вместо list.sort(), явно выбор дизайна , на мой взгляд, хороший. Как сказано выше, List не может обеспечить безопасность во время компиляции. Таким образом, единственный выбор - go для обеспечения безопасности во время выполнения, что не так хорошо.

Наличие list.sort(), которое работает только иногда и выдает исключение, в противном случае это будет странный дизайн выбор. Однако звонок типа list.sort(null) намного понятнее любому пользователю. В частности, более ясно, что это будет охватываться решениями во время выполнения, а не проверками во время компиляции.

Вы можете предпочесть сделать это явным и дать ему тривиальный Comparator, который использует естественный порядок:

list.sort(Comparator.naturalOrder());

Это было бы по крайней мере еще понятнее для читателя.


История

Теперь вы можете задаться вопросом, почему List.sort(null) даже является поддерживаемой функцией и почему они не требуют от вас явного Comparator всегда. Я не могу заглянуть в мысли разработчиков, но я подозреваю, исторические причины. Подобных примеров в JDK довольно много, особенно с сортировкой.

Это главным образом потому, что Java поддерживает обратную совместимость. Обобщения были введены с Java 5, поэтому все, что существовало до этого, имело дело с контейнерами, не было разработано с учетом безопасности типов.

Есть несколько тесно связанных примеров:

  • Arrays#sort(Object[]) (с Java 1.2) не принудительно Comparable. Поэтому им пришлось добавить дополнительную перегрузку в Java 5 Arrays.sort(T[], Comparator), точно так же, как в примере List.
  • Метод Collections.sort(T[], Comparator) фактически принимает null для компаратора. Плохой дизайн, но он был сделан тогда, когда дженерики еще не существовали. Поэтому они больше не могли удалять эту функцию .
  • Интересно, что они решили разрешить List.sort(Comparator), новый метод, также поддерживающий null. Однако в Stream.sort(Comparator) они этого не сделали. Это странное несоответствие в JDK.
2 голосов
/ 15 апреля 2020

Когда вы не предоставляете компаратор, используется порядок natural, который применим только для типов, которые реализуют Comparable.

Если вы посмотрите на определение метода для Collections.sort():

    public static <T extends Comparable<? super T>> void sort(List<T> list)

Тип элемента ограничен так, что он должен реализовывать Comparable.

A List не имеет этого ограничения, так как существует множество случаев, когда вы хотите, чтобы список содержал объекты без естественного порядка. Таким образом, вы не можете вызвать sort(), потому что List не может гарантировать, что он содержит элементы с естественным упорядочением.

Вы можете проверить это:

Следующее ошибка компиляции:

    List<Object> objs = new ArrayList<>();
    objs.add(new Object());
    objs.add(new Object());

    Collections.sort(objs);

, поскольку Object не реализует Comparable<Object>, разумного способа их сортировки нет, если вы не предоставите компаратор.

Если я обойду Collections класс и просто сделайте это:

    List<Object> objs = new ArrayList<>();
    objs.add(new Object());
    objs.add(new Object());

    objs.sort(null);

Я получаю исключение, которое объясняет то же самое:

    java.lang.ClassCastException: class java.lang.Object cannot be cast to class java.lang.Comparable
1 голос
/ 15 апреля 2020

Я не понимаю логику c, почему list.sort () не имеет версии без компаратора?

Я тоже не понимаю, но я предполагаю, что это просто личное предпочтение программиста, который ввел этот метод.

Поскольку Java 1.8 поддерживает методы по умолчанию в интерфейсах, было бы легко добавить перегруженную версию без параметра List.sort() в поясните, что параметр является необязательным. Этот метод можно просто делегировать методу sort(Comparator).

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

Мое простое правило:

  • Метод указывает параметр, потому что он ему нужен. Таким образом, он является обязательным по умолчанию.
  • Предоставьте перегруженный метод без этого параметра, если он необязательный.

Также я не уверен, что в этой ситуации лучше вызывать список .sort (null), чтобы избежать лишнего вызова, или все же предпочтительнее collection.sort () и избегает безобразного аргумента null.

Я бы об этом не беспокоился, потому что если этот дополнительный вызов - это ваша проблема, чем вы действительно в беде. Поэтому выберите вариант, который вы считаете более понятным. Также возможно, что JIT-компилятор удаляет вызов путем встраивания.

...