web-dev-qa-db-fra.com

Java flatMap sur la liste de la liste des entiers optionnels

J'ai le peu de code simplifié suivant qui ne compile pas et je ne comprends pas pourquoi:

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

Le compilateur me dit:

[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

J'avoue que je ne suis pas habitué à Java mais que je dois le faire pour un projet. Je me suis moqué de cela dans Scala où list.flatten et l'équivalent list.flatMap(i => i) fonctionnent exactement comme prévu:

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

La variable flatMap de Java est-elle différente?

8
Max Power

CA devrait etre:

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

Votre flatMap attend une fonction qui transforme un élément Stream en un Stream. Vous devez utiliser map à la place (pour extraire la valeur de Optional). De plus, vous devez filtrer les Optionals vides (sauf si vous souhaitez les transformer en nulls).

Sans le filtrage:

List<Integer> flattened = 
  list
    .stream()
    .map(o -> o.orElse(null))
    .collect(Collectors.toList());
14
Eran

Documentation of Stream.flatMap(Function<? extends T, ? extends Stream<? extends R>>):

Renvoie un flux comprenant les résultats du remplacement de chaque élément de ce flux par le contenu d'un flux mappé produit en appliquant la fonction de mappage fournie à chaque élément. Chaque flux mappé est fermé une fois que son contenu a été placé dans ce flux. (Si un flux mappé est null, un flux vide est utilisé à la place.) C'est une opération intermédiaire.

API Note:

L'opération flatMap() a pour effet d'appliquer une transformation un à plusieurs aux éléments du flux, puis d'aplatir les éléments résultants dans un nouveau flux.

Comme vous pouvez le constater, la méthode est utilisée pour prendre chaque élément, créer un flux, puis aplatir chaque flux en un seul flux (qui est renvoyé par la méthode). Alors faisant:

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

Ne fonctionnera pas car la fonction retourne une Optional et non une Stream de Integers. Pour résoudre ce problème, vous devez modifier la fonction afin de renvoyer une Stream de ce que contient la Optional. Selon la version de Java que vous utilisez, vous pouvez effectuer les tâches suivantes:

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());

D'autres options incluent l'utilisation de map combiné à filter ou de méthodes de Optional. Voir Réponse d'Eran pour des exemples.

2
Slaw

Le flatMap de Java est-il différent?

La méthode flatMap s'attend à ce que la fonction fournie renvoie un flux, mais i -> i ne le fournit pas. Dans JDK8, vous devrez créer un flux à partir du fichier facultatif, puis aplatir:

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

ou dans JDK9, dans peut être fait comme:

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