Потоки Java 8: Почему mapToInt нужен Integer :: parseInt в качестве параметра? - PullRequest
1 голос
/ 06 июля 2019

Я пытаюсь что-то понять, следуя следующему примеру:

    Stream.of("a1", "a2", "a3")
            .map(s -> s.substring(1))
            .mapToInt(Integer::parseInt)
            .max()

     . (etc.)

Зачем mapToInt нужен

    Integer::parseInt 

как параметр? Разве это не должно быть неявным для этого? Разве этот параметр не избыточен?

Спасибо!

Ответы [ 5 ]

2 голосов
/ 06 июля 2019

Это не избыточно.Вы должны сказать, как преобразовать (скажем) строку «1» в целое число 1.

(я думаю, вы могли бы утверждать, что метод mapToLong без аргументов был бы удобен, но дизайнеры этого не сделаливключите один в API.)


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

Если вы пытаетесь применить Stream::max кПоток чисел, представленных в виде строк, вы получите лексически наибольшее значение, а не численно наибольшее значение.Например, «9» больше, чем «11».

Обратите внимание, что метод Stream::max не имеет перегрузки без аргументов.Вам нужно будет предоставить компаратор;например,

Stream.of("a1", "a2", "a3")
    .map(s -> s.substring(1))
    .max(compareAsIntegers)

, где

Comparator<String> compareAsIntegers = new Comparator<String>() {
    public int compare (String s1, String s2) {
        return Integer.compare(Integer.parseInt(s1),
                               Integer.parseInt(s2));
    }
}
1 голос
/ 07 июля 2019

Важно различать, что делает вызов Stream#mapToInt(ToIntFunction) и что делает аргумент ToIntFunction.

  • Вызов mapToInt - это , что будет делать поток (т.е. отобразить элементы на int).
  • Аргумент ToIntFunction равен как поток собирается сопоставить каждый элемент с int.

Могли бы они включить метод без аргументов mapToInt, который неявно анализирует String с int с? Да, но посмотрите, насколько хорошо это работает для Stream#sorted() - и эта ситуация далеко не так произвольна или неоднозначна, как метод без аргументов mapToInt.

Метод no-arg sorted предполагает, что элементы Comparable являются фундаментальным, стандартизированным и широко распространенным интерфейсом в Java - любой класс может реализовать интерфейс (тогда как есть только один класс, который может быть * 1035). *). Таким образом, хотя метод не является типобезопасным, можно утверждать, что прецедент достаточно распространен, чтобы оправдать его существование.

Тем не менее, метод no-arg mapToInt, который предполагает, что элементы равны String s и анализирует их содержимое в int, является весьма специфическим вариантом использования. Почему такой общий API предлагает такую ​​специфическую операцию? Это было бы совершенно произвольное решение без разумного обоснования. Например, почему бы не сопоставить каждый String с length? И почему String обрабатывается специально, а не какой-то другой тип (типы)? Следовательно, метод принимает аргумент ToIntFunction, который описывает как для сопоставления элементов с int на индивидуальной основе.


Может быть полезно отметить, что Integer::parseInt - это ссылка на метод , что является сокращением для тривиального лямбда-выражения . Оба являются реализацией функционального интерфейса , который является менее подробным вариантом, чем использование анонимного класса . Другими словами, вы создаете новый экземпляр ToIntFunction и передаете его в качестве аргумента.

Все перечисленные ниже функционально эквивалентны:

// method reference
stream.mapToInt(Integer::parseInt)

// lambda expression
stream.mapToInt((String value) -> Integer.parseInt(value)) // explicit parameter types
stream.mapToInt(value -> Integer.parseInt(value))          // implicit parameter types

// anonymous class
stream.mapToInt(new ToIntFunction<>() {

    @Override
    public int applyAsInt(String value) {
        return Integer.parseInt(value);
    }

})

Где stream является Stream<String>.

1 голос
/ 06 июля 2019

операции, которые вы делаете, похожи на:

Stream.of("a1", "a2", "a3")           //step1
        .map(s -> s.substring(1))     //step2
        .mapToInt(Integer::parseInt)  //step3
        .max()

Вы определяете поток строк (на шаге 1), затем вы берете каждый из них и сбрасываете 1 символ на нем, оставляя поток «1», «2» и «3», (шаг 2), но обратите внимание, что эти все еще строковые объекты ... затем вы конвертируете это в целые числа, поэтому вам нужно дать 'mapToInt' «функцию», которая принимает строку в качестве параметра и возвращает целое число (step3)

и это определено в классе Integer:

https://docs.oracle.com/javase/7/docs/api/java/lang/Integer.html#parseInt(java.lang.String)

вы наверняка можете написать и передать свою собственную функцию ... но зачем изобретать велосипед? :)

1 голос
/ 06 июля 2019

substring возвращает строку, а не целое число. mapToInt преобразует поток объектов (в данном случае Strings) в IntStream примитивов int. Однако он не знает, как сделать отображение, вам нужно предоставить ему функцию, которая в вашем случае будет parseInt.

0 голосов
/ 07 июля 2019

ну собственно, после прочтения добрых ответов тут я понял 1. Чтобы мой вопрос был недостаточно ясен. 2. Ответ ...

Итак,

  1. Что я действительно имел в виду, так это - не передать ли Integer :: parseInt в качестве аргумента избыточности mapToInt? Поскольку я чувствовал, что mapToInt точно описывает цель метода - отображение объекта в int, и добавление parseInt излишне, потому что оно на самом деле одно и то же - анализирует объект в int ...

  2. Но затем я попытался передать также Integer: valueOf, и он также был принят - так что я понял, что есть пара вариантов для передачи в mapToInt, и это ответило на мой вопрос ...

...