Поиск Max с выражением Lambda в Java

Это мой код

    List<Integer> ints = Stream.of(1,2,4,3,5).collect(Collectors.toList());
    Integer maxInt = ints.stream()
                              .max(Comparator.comparing(i -> i))
                              .get();

    System.out.println("Maximum number in the set is " + maxInt);

выход:

Maximum number in the set is 5

Я не могу сделать distingues между двумя я в следующем разделе моего кода

Comparator.comparing(i -> i)

может ли кто-нибудь быть добрым и объяснить разницу между двумя i?

Ответ 1

Метод Comparator.comparing(…) предназначен для создания Comparator, который использует порядок, основанный на свойстве объектов для сравнения. При использовании выражения lambda i -> i, которое является короткой записью для (int i) -> { return i; } здесь, в качестве функции поставщика свойств, полученный Comparator будет сравнивать сами значения. Это работает, когда объекты для сравнения имеют естественный порядок, если Integer имеет.

Итак,

Stream.of(1,2,4,3,5).max(Comparator.comparing(i -> i))
.ifPresent(maxInt->System.out.println("Maximum number in the set is " + maxInt));

делает то же самое, что и

Stream.of(1,2,4,3,5).max(Comparator.naturalOrder())
.ifPresent(maxInt->System.out.println("Maximum number in the set is " + maxInt));

хотя последний более эффективен, поскольку он реализован как singleton для всех типов, которые имеют естественный порядок (и реализуют Comparable).

Причина, по которой max требует Comparator вообще, заключается в том, что вы используете общий класс Stream, который может содержать произвольные объекты.

Это позволяет, например, использовать его как streamOfPoints.max(Comparator.comparing(p->p.x)), чтобы найти точку с наибольшим значением x, а сам Point не имеет естественного порядка. Или сделайте что-нибудь вроде streamOfPersons.sorted(Comparator.comparing(Person::getAge)).

При использовании специализированного IntStream вы можете использовать естественный порядок, который может быть более эффективным:

IntStream.of(1,2,4,3,5).max()
.ifPresent(maxInt->System.out.println("Maximum number in the set is " + maxInt));

Чтобы проиллюстрировать разницу между "естественным порядком" и порядком на основе свойств:

Stream.of("a","bb","aaa","z","b").max(Comparator.naturalOrder())
.ifPresent(max->System.out.println("Maximum string in the set is " + max));

это напечатает

Максимальная строка в наборе равна z

поскольку естественный порядок String является лексикографическим порядком, где z больше, чем b, которое больше, чем a

С другой стороны,

Stream.of("a","bb","aaa","z","b").max(Comparator.comparing(s->s.length()))
.ifPresent(max->System.out.println("Maximum string in the set is " + max));

напечатает

Максимальная строка в наборе - aaa

как aaa имеет максимальную длину всего String в потоке. Это предполагаемый прецедент для Comparator.comparing, который можно сделать еще более читаемым при использовании ссылок на методы, т.е. Comparator.comparing(String::length), который почти говорит сам за себя...

Ответ 2

Эта функция (примечание -> предназначена для замыканий и не путается с =>, которая предназначена для сравнения)

i -> i

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

Менее тривиальный пример может быть

max(Comparator.comparing(i -> -i))

который даст вам минимум или

max(Comparator.comparing(i -> Math.abs(100-i))

дает вам значение, самое большее от 100.

max(Comparator.comparing(i -> i.toString()))

который даст вам максимальное сравнение как String i.e. "9" > "10" в виде строки.

Ответ 3

Comparator.comparing ожидает функцию, которая сопоставляет исходный объект со значением, которое фактически сравнивается - в вашем случае, поскольку вы не хотите предварительно обрабатывать сравниваемое значение, i просто сопоставляется с самим собой.