Рубрики
Без рубрики

Окна… Окна…

Погружение в четырнадцатую главу Эффективной Java. С пометкой java, эффективная, сопоставимая архитектура.

Сегодня мы переходим к заключительной главе этого раздела о методах, общих для всех объектов. В отличие от остальных методов, о которых говорилось в этом разделе, этот метод на самом деле не является методом класса Объект . При этом этот метод действительно влияет на операции по умолчанию в различных других классах, поэтому важно понимать. Метод, о котором мы говорим сегодня, – это сравнение с и это связанный интерфейс, Comparable .

Итак, какова цель интерфейса Comparable ? У него есть цель в соответствии с Object ‘s equals однако его цель состоит в том, чтобы выполнять сравнения порядка, а также сравнения равенства. Когда вы реализуете Comparable |/вы указываете, что в ваших экземплярах существует естественный порядок, и предлагаете способ организовать их таким образом. Как только вы реализуете интерфейс, сортировка массива из них так же проста, как Arrays.sort(myArray) .

Учитывая, что естественный порядок может быть определен, это делает тривиальным сохранение отсортированной коллекции, поиск по значениям или поиск максимальных и минимальных значений. Например, заполнение TreeSet (который использует метод compareTo для сортировки своей внутренней структуры данных) с помощью объектов String (которые реализуют Comparable ) вы получаете список значений в алфавитном порядке. Интерфейс Comparable обеспечивает большую ценность, вероятно, именно поэтому практически все встроенные типы значений Java реализуют этот интерфейс. Если класс значений, который вы пишете, имеет естественный порядок, может быть хорошей идеей также реализовать этот интерфейс.

Давайте рассмотрим интерфейс и контракт. Интерфейс Comparable выглядит примерно следующим образом:

public interface Comparable {
  int compareTo(T t);
}

Это обеспечивает довольно простой интерфейс. Один метод, который принимает универсальный тип и возвращает целое число. Итак, давайте рассмотрим контракт для метода compareTo :

  • Метод compareTo должен возвращать отрицательное число, ноль или положительное число, поскольку объект меньше, равен или больше предоставленного объекта.
  • Вызовите исключение ClassCastException , если предоставленный тип объекта несовместим для сравнения с объектом.

Следующие несколько элементов используют преимущества математической функции signum , обозначаемой ниже как sign() . Проще говоря, этот метод возвращает значение -1 для отрицательных чисел, 0 для 0 и 1 для положительных чисел.

  • Для всех x и y , sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
  • В связи с вышесказанным x.compareTo(y) должен выдавать исключение только в том случае, если y.compareTo(x) также выдает исключение.
  • То же, что и функция equals , compareTo должен быть переходным. Следовательно, если x.compareTo(y) > 0 && y.compareTo(z) > 0 тогда x.compareTo(z) > 0 . Это также должно работать с < и ==

Сравнение между сравнением С и равно уже упоминалось ранее в этом посте, и сравнение с возвращает 0 когда элементы равны так должно быть, когда равно возвращает true следует сравните С всегда возвращайте 0 ? Вопрос сводится к тому, приравнивается ли равенство объектов к естественному упорядочению? Часто бывает так, что это одно и то же, и может быть удивительно, когда они не совпадают.

Давайте посмотрим на пример того, где это может быть удивительно. В Класс BigDecimal реализует comparable способом, отличным от его реализации equals . Итак, дан HashSet (который использует преимущества метода equals ) с двумя элементами new BigDecimal("1.0") и новый BigDecimal("1.00") . HashSet в конечном итоге будет содержать две записи в нем. Сравните это с помещением тех же двух элементов в TreeSet/| (который использует метод compareTo ), и в итоге мы получим только один элемент в коллекции. Не совсем соответствует принципу наименьшего удивления, не так ли? Таким образом, даже если это не требуется, настоятельно рекомендуется, чтобы, когда два объекта возвращали true в equals , те же два объекта в конечном итоге возвращали 0 из Сравнение . По крайней мере, если этому предложению не последуют, должно быть хорошо задокументировано, что оно не соответствует этим ожиданиям.

Итак, как же можно написать метод compareTo ? Это не слишком отличается от написания метода equals .

  1. Определите порядок значимости полей класса.
  2. Сравните поля, либо рекурсивно вызывая методы compareTo для ссылочных типов, либо используя один из встроенных Упакованный Time.compare() методы, такие как Double.compare() .
  3. Как только вы найдете неравное поле, верните значение для этого поля (или, если различий нет, верните 0 ).

Давайте рассмотрим пример:

public int compareTo(PhoneNumber pn) {
  int result = Short.compare(areaCode, pn.areaCode);
  if (result == 0) {
    result = Short.compare(prefix, pn.prefix);
    if (result == 0) {
      result = Short.compare(lineNumber, pn.lineNumber);
    }
  }
  return result;
}

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

private static final Comparator COMPARATOR = 
  comparingInt((PhoneNumber pn) -> pn.areaCode)
    .thenComparingInt(pn -> pn.prefix)
    .thenComparingInt(pn -> pn.lineNum);

public int compareTo(PhoneNumber pn) {
  return COMPARATOR.compare(this, pn);
}

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

Что-то, что вы иногда увидите, что может показаться хорошей идеей в то время, – это использование того факта, что разница между двумя значениями отрицательна. Таким образом, вычитание одного из другого может привести к выполнению контракта. Проблема в том, что вы можете иметь дело с переполнением целых чисел и другими подобными проблемами. Лучше всего воспользоваться небольшим снижением производительности при использовании методов сравнения.

К сожалению, с помощью этого метода я не знаю такого инструмента, как Lombok , который мог бы сгенерировать для нас метод compareTo . В таком случае нам действительно нужно самим заняться созданием этого метода. Подводя итог этой главе, можно сказать, что при создании типа значения с естественным порядком вам следует рассмотреть возможность реализации интерфейса Comparable . Это позволяет легко сортировать, искать и использовать ваш тип значения в коллекциях на основе сравнения.

Оригинал: “https://dev.to/kylec32/effective-java-tuesday-consider-implementing-comparable-3m9j”