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?
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 Optional
s vides (sauf si vous souhaitez les transformer en null
s).
Sans le filtrage:
List<Integer> flattened =
list
.stream()
.map(o -> o.orElse(null))
.collect(Collectors.toList());
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 Integer
s. 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.
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());