FlatMap Java в списке списка необязательных целых чисел - PullRequest
0 голосов
/ 14 декабря 2018

У меня есть следующий бит упрощенного кода, который не компилируется, и я не понимаю, почему:

List<Optional<Integer>> list =
    new ArrayList<>();
List<Integer> flattened = 
  list
    .stream()
    .flatMap(i -> i)
    .collect(Collectors.toList());

Компилятор говорит мне:

[ERROR] ... incompatible types: cannot infer type-variable(s) R
[ERROR]     (argument mismatch; bad return type in lambda expression
[ERROR]       Optional<Integer> cannot be converted to Stream<? extends R>)
[ERROR]   where R,T are type-variables:
[ERROR]     R extends Object declared in method <R>flatMap(Function<? super T,? extends Stream<? extends R>>)
[ERROR]     T extends Object declared in interface Stream

Я признаю, что я 'Я не привык к Java, но мне нужно для проекта.Я высмеял это в Scala, где list.flatten и эквивалент list.flatMap(i => i) работают так же, как и ожидалось:

val list = List(Some(1), Some(2), None)
list.flatten // List(1, 2)

Отличается ли Java flatMap от других?

Ответы [ 3 ]

0 голосов
/ 14 декабря 2018

Это должно быть:

List<Integer> flattened = 
  list
    .stream()
    .filter (Optional::isPresent)
    .map(Optional::get)
    .collect(Collectors.toList());

Ваш flatMap ожидает функцию, которая преобразует элемент Stream в Stream.Вместо этого вы должны использовать map (чтобы извлечь значение Optional).Кроме того, вам необходимо отфильтровать пустые Optional с (если вы не хотите преобразовать их в null с).

Без фильтрации:

List<Integer> flattened = 
  list
    .stream()
    .map(o -> o.orElse(null))
    .collect(Collectors.toList());
0 голосов
/ 14 декабря 2018

Документация из Stream.flatMap(Function<? extends T, ? extends Stream<? extends R>>):

Возвращает поток, состоящий из результатов замены каждого элемента этого потока содержимым сопоставленного потока, созданного с применением предоставленногофункция отображения на каждый элемент.Каждый сопоставленный поток закрывается после помещения его содержимого в этот поток.(Если сопоставленный поток равен null, вместо него используется пустой поток.) ​​Это промежуточная операция.

Примечание API:

Операция flatMap()имеет эффект применения преобразования «один ко многим» к элементам потока, а затем сглаживания результирующих элементов в новый поток.

Как видите, метод используется дляДля каждого элемента создайте из них stream , затем сведите каждый поток в один поток (который возвращается методом).Таким образом:

List<Optional<Integer>> optionals = ...;
List<Integer> integers = optionals.stream()
        .flatMap(optional -> optional) // identity function
        .collect(Collectors.toList());

Не будет работать, поскольку функция возвращает Optional, а не Stream из Integer с.Чтобы решить эту проблему, вам нужно изменить функцию, чтобы она возвращала Stream того, что содержит Optional.В зависимости от используемой версии Java вы можете:

Java 9 +,

List<Optional<Integer>> optionals = ...;
List<Integer> integers = optionals.stream()
        .flatMap(Optional::stream)
        .collect(Collectors.toList());

Java 8,

// There are different ways to convert an Optional into a Stream using
// flatMap, this is just one option. Holger shows other ways in the comments.
List<Optional<Integer>> optionals = ...;
List<Integer> integers = optionals.stream()
        .flatMap(optional -> optional.isPresent() ? Stream.of(optional.get()) : Stream.empty())
        .collect(Collectors.toList());

Другие параметрывключают использование map в сочетании с filter или методы Optional.См. ответ Эрана для примеров.

0 голосов
/ 14 декабря 2018

Отличается ли flatMap от Java?

Метод flatMap ожидает, что предоставленная функция возвращает поток, но i -> i этого не обеспечивает.В JDK8 вам нужно создать поток из Optional, а затем сгладить:

 list.stream()
     .flatMap(i -> i.isPresent() ? Stream.of(i.get()) : Stream.empty())
     .collect(Collectors.toList());

или в JDK9, это можно сделать так:

  list.stream()
      .flatMap(Optional::stream)
      .collect(Collectors.toList());
...