Компаратор Java Arrays.sort () - PullRequest
       33

Компаратор Java Arrays.sort ()

2 голосов
/ 26 сентября 2019

Я хочу отсортировать array двумерных массивов в Java в соответствии с некоторыми правилами, скажем, по расстоянию от источника.Я видел несколько способов сделать это, используя Arrays.sort():

1) Arrays.sort(points, Comparator.comparing(p -> p[0]*p[0] + p[1]*p[1]));

2) Arrays.sort(points, (p1, p2) -> p1[0]*p1[0] + p1[1]*p1[1] - p2[0]*p2[0] - p2[1]*p2[1]);

3) Определение class:

class Point implements Comparable<Point>{
     // class variables, constructor
     public int compareTo(Point p) {
         return (x*x + y*y).compareTo(p.x*p.x + p.y*p.y);
     }
}

Затем создается массив pts типа Points и используется Arrays.sort(pts).Мой вопрос касается 1) и 2) : я вижу разницу между, но я не понимаю, когда использовать один и когда использовать другой, и что именно Comparator.comparing()делает.Почему первое решение переносит информацию только с одной точкой, а второе требует двух точек?

Ответы [ 3 ]

1 голос
/ 26 сентября 2019

Используя Comparator.comparing(), вы говорите: «Я хочу использовать это свойство при сравнении объектов этого класса».Свойство должно быть Comparable, поэтому вы часто видите, что оно используется с числовыми свойствами, например: Comparator.comparing(MyObject::getId) возвращает Comparator с логикой «сравнивать объекты MyObjects, используя их числовой идентификатор».

с(p1, p2) -> Вы делаете больше работы сами.Вам дано 2 объекта, и вам нужно вернуть -1, 0 или +1 в зависимости от того, меньше p1, равно или больше p2.Это допускает более сложную логику, но является более подробным и менее читаемым, чем первый вариант, если вы сортируете по одному или нескольким свойствам.

Наконец, вы заставляете сам класс реализовать Comparable.Обычно это называется естественным порядком.У Point на самом деле нет порядка натурального , как, например, чисел, поэтому я не советую создавать метод compareTo.

1 голос
/ 26 сентября 2019

В 2) необходимо сделать сравнение между обоими операндами сравнения по некоторой функции.В 1) просто указывается одна функция, которая доставляет сравниваемое значение.Помимо comparing здесь также есть числовые значения (как здесь) comparingInt, comparingLong, comparingDouble.

Итак, 1) проще всего читать.

Для точек с двойными числами можно использовать гипотенузу,фактическое расстояние до начала координат

Arrays.sort(points, Comparator.comparingDouble(p -> Math.hypot(p[0], p[1]));

А потом плохие новости:

Двумерная сортировка расстояний не дает последовательного порядка .Следовательно, сортировка не будет работать между двумя элементами.Вы можете сортировать только по одной фиксированной точке.Скажем, ранжируйте точки массива относительно некоторой фиксированной точки.Здесь вы берете расстояние до начала координат (0, 0).

1 голос
/ 26 сентября 2019

Comparator.comparing реализован следующим образом:

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
        Function<? super T, ? extends U> keyExtractor)
{
    Objects.requireNonNull(keyExtractor);
    return (Comparator<T> & Serializable)
        (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}

т.е. он использует Function, который вы передаете ему, чтобы преобразовать каждый из сравниваемых элементов в Comparable, а затем используетComparable s compareTo метод.

Когда вы передаете функцию p -> p[0]*p[0] + p[1]*p[1], вы конвертируете каждую точку в ее сумму квадратов.Затем, когда Comparator необходимо сравнить две точки, он сравнивает сумму квадратов двух точек, что почти эквивалентно вычислению разницы сумм квадратов двух точек (это не совсем эквивалентно, поскольку сравнение двухчисла путем вычисления их разности могут привести к неправильному выводу в случае переполнения чисел).

Это именно то, что делает ваш второй Comparator - (p1, p2) -> p1[0]*p1[0] + p1[1]*p1[1] - p2[0]*p2[0] - p2[1]*p2[1] -

с p1[0]*p1[0] + p1[1]*p1[1] - p2[0]*p2[0] - p2[1]*p2[1] == (p1[0]*p1[0] + p1[1]*p1[1]) - (p2[0]*p2[0] + p2[1]*p2[1]).

Использование Comparator.comparing() более безопасно, поскольку оно сравнивает суммы квадратов двух точек без вычисления их разности.Вместо этого он использует Double compareTo() (при условии, что координаты ваших точек равны Double или double).

Другими словами, в первой альтернативе используется Function, для которого требуется всего одинточка, поскольку эта функция сообщает Comprator.comparing, как преобразовать каждую из 2 точек.

С другой стороны, вторая альтернатива принимает 2 точки (которые являются обязательными аргументами метода Comparator.compare()) и определяетОтносительный порядок этих 2 баллов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...