web-dev-qa-db-fra.com

Retourne un élément vide de Java 8 opération de carte

Utiliser Java 8 stream ce qui est la meilleure façon pour mapper un List<Integer> lorsque vous n'avez pas de sortie pour l'entrée Integer?

Renvoyez simplement null? Mais maintenant, la taille de ma liste de sortie sera plus petite que ma taille d'entrée ...

    List<Integer> input = Arrays.asList(0,1,2,3);
    List<Integer> output = input.stream()
                                .map(i -> { 
                                    Integer out = crazyFunction(i);
                                    if(out == null || out.equals(0))
                                        return null;
                                    return Optional.of(out);
                                    })
                                .collect(Collectors.toList());
25
Martin Magakian

Je ne comprends pas pourquoi vous (et toutes les réponses) compliquez les choses. Vous avez une opération de mappage et une opération de filtrage. La manière la plus simple consiste donc à appliquer ces opérations l'une après l'autre. Et à moins que votre méthode ne retourne déjà un Optional, il n'est pas nécessaire de traiter Optional.

input.stream().map(i -> crazyFunction(i))
              .filter(out -> out!=null && !out.equals(0))
              .collect(Collectors.toList());

Il peut être simplifié pour

input.stream().map(context::crazyFunction)
              .filter(out -> out!=null && !out.equals(0))
              .collect(Collectors.toList());

Mais vous semblez avoir une question plus théorique sur le type de List à générer, un avec des espaces réservés pour les valeurs absentes ou un avec une taille différente de la liste d'entrée.

La réponse simple est: ne pas générer de liste. Un List n'est pas une fin en soi, vous devez donc considérer pour quel type de opération vous avez besoin de cette liste (ou de son contenu) et appliquer l'opération correctement comme opération terminale du flux . Ensuite, vous avez votre réponse car l'opération dicte si les valeurs absentes doivent être filtrées ou représentées par une valeur spéciale (et quelle valeur doit être).

Ce pourrait être une réponse différente pour différentes opérations…

48
Holger

Remplacez l'appel map par flatMap. L'opération map produit une valeur de sortie par valeur d'entrée, tandis que l'opération flatMap produit un nombre quelconque de valeurs de sortie par valeur d'entrée - inclut zéro.

Le moyen le plus simple est probablement de remplacer le chèque comme ceci:

List<Integer> output = input.stream()
                            .flatMap(i -> { 
                                Integer out = crazyFunction(i);
                                if (out == null || out.equals(0))
                                    return Stream.empty();
                                else
                                    return Stream.of(out);
                                })
                            .collect(Collectors.toList());

Un refactoring supplémentaire pourrait changer crazyFunction pour qu'il retourne un Optional (probablement OptionalInt). Si vous l'appelez depuis map, le résultat est un Stream<OptionalInt>. Ensuite, vous devez flatMap ce flux pour supprimer les options vides:

List<Integer> output = input.stream()
    .map(this::crazyFunctionReturningOptionalInt)
    .flatMap(o -> o.isPresent() ? Stream.of(o.getAsInt()) : Stream.empty())
    .collect(toList());

Le résultat de flatMap est un Stream<Integer> qui contient les ints, mais c'est OK puisque vous allez les envoyer dans un List. Si vous n'alliez pas encadrer les valeurs int en List, vous pourriez convertir les Stream<OptionalInt> vers un IntStream en utilisant ce qui suit:

flatMapToInt(o -> o.isPresent() ? IntStream.of(o.getAsInt()) : IntStream.empty())

Pour plus de détails sur la gestion des flux d'options, voir cette question et ses réponses .

30
Stuart Marks

Variantes plus simples de la réponse de @Martin Magakian:

List<Integer> input = Arrays.asList(0,1,2,3);
List<Optional<Integer>> output =
  input.stream()
    .map(i -> crazyFunction(i)) // you can also use a method reference here
    .map(Optional::ofNullable) // returns empty optional
                               // if original value is null
    .map(optional -> optional.filter(out -> !out.equals(0))) // return empty optional
                                                           // if captured value is zero
    .collect(Collectors.toList())
;

List<Integer> outputClean =
  output.stream()
    .filter(Optional::isPresent)
    .map(Optional::get)
    .collect(Collectors.toList())
;
2
srborlongan

Vous pouvez encapsuler la sortie dans un Facultatif qui peut ou non contenir une valeur non nulle.
Avec une sortie: return Optional.of(out);
Sans sortie: return Optional.<Integer>empty();

Vous devez encapsuler dans une option car un tableau ne peut contenir aucune valeur nulle .

    List<Integer> input = Arrays.asList(0,1,2,3);
    List<Option<Integer>> output = input.stream()
                                .map(i -> { 
                                    Integer out = crazyFunction(i);
                                    if(out == null || out.equals(0))
                                        return Optional.<Integer>empty();
                                    return Optional.of(out);
                                    })
                                .collect(Collectors.toList());

Cela garantira que input.size() == output.size().

Plus tard, vous pouvez exclure l'option facultative vide en utilisant:

    List<Integer> outputClean = output.stream()
                                   .filter(Optional::isPresent)
                                   .map(i -> {
                                           return i.get();
                                        })
                                   .collect(Collectors.toList());
1
Martin Magakian