web-dev-qa-db-fra.com

Utilisation de l'API de flux Java 8 ou autre

Ce que j'essaie de faire est de filtrer la liste, puis de la mapper et d'utiliser orElse si null, puis de la récupérer dans la liste. Maintenant, je peux y arriver de cette façon:

return users.stream()
    .filter(user -> id.equals(user.getId()))
    .map(
        user -> {
            if(user.getData() != null) {
                return user.getData();
            }
            return Collections.emptyMap();
        }
    )
    .collect(Collectors.toList());

Mais la question est: comment puis-je améliorer cette structure et pourquoi ne puis-je pas utiliser orElse dans ce cas?

16
Mykyta Bezverkhyi

Il pourrait être plus lisible avec l'opérateur conditionnel ternaire:

return users.stream()
    .filter(user -> id.equals(user.getId()))
    .map(
        user -> (user.getData() != null) 
        ? user.getData() 
        : emptyMap()
    )
    .collect(Collectors.toList())
;

Pour utiliser orElse, vous devez créer une Optional qui enveloppe user.getData(). Je ne suis pas sûr que ce soit une bonne idée.

Si vous insistez pour utiliser orElse (ou même mieux, orElseGet, pour éviter d'évaluer emptyMap() quand ce n'est pas nécessaire), cela peut ressembler à ceci:

return users.stream()
    .filter(user -> id.equals(user.getId()))
    .map(
        user -> Optional.ofNullable(
            user.getData()
        ).orElseGet(
            () -> emptyMap()
        )
    )
    .collect(Collectors.toList())
;
14
Eran

Comme je l'ai également souligné dans les commentaires et je doute fort que vous cherchiez peut-être les éléments suivants

users
    .stream()
    .filter(
        user -> id.equals(user.getId()) 
        && (user.getData() != null)
    )
    .map(User::getData)
    .collect(Collectors.toList())
;

Mais alors la question n’est pas assez claire pour dire quel est le type de retour éventuel de votre déclaration ou quelle est la emptyMap utilisée dans votre code! Par conséquent, je doute fortement que vous ayez besoin d’une API Optional en premier lieu pour cette opération.

Remarque: La solution indiquée ci-dessus suppose que emptyMap est Collections.emptyMap, ce que je ne sais pas pourquoi vouloir collecter dans une structure de données notée List<Map<K,V>>.

4
nullpointer

Comment puis-je améliorer cette structure 

Méthode 1:

return users.stream()
    .filter(user -> id.equals(user.getId()))
    .map(
        user -> (user.getData() != null)
        ? user.getData() 
        : emptyMap()
    )
    .collect(Collectors.toList())
;

Méthode 2:

Faites en sorte que votre getData retourne une Optional: user -> user.getData().orElse(emptyMap())

Méthode 3:

Comme @Eran a déclaré: Optional.ofNullable, utilisez ensuite orElse(emptyMap()) comme ci-dessus: user -> Optional.ofNullable(user.getData()).orElse(emptyMap())

Pourquoi je ne peux pas utiliser orElse dans ce cas?

Vous ne savez pas ce que vous voulez dire orElse

  1. Si user.getData() renvoie null, il doit être encapsulé dans une Optional pour appeler orElse.

  2. La findAny().orElse du flux agit sur le résultat du flux lui-même. Mais ce dont vous avez besoin ici, c’est de vérifier si user.getData() existe. Vous ne pouvez donc pas utiliser directement la variable orElse du résultat de stream.

1
shawn

Utilisez Objects::requireNonNullElse !

Je conseillerais deux choses pour rendre le code plus lisible. Cependant, je ne voudrais pas introduire artificiellement une Optional.


Première option: Objects::requireNonNullElse dans une méthode séparée

List<Map<?, ?> bar() {
    //...

    return users.stream()
                .filter(user -> id.equals(user.getId()))
                .map(User::getData)
                .map(Foo::nullSafeMap)
                .collect(Collectors.toList());
}

private static Map<?, ?> nullSafeMap(final Map<?, ?> map) {
    return Objects.requireNonNullElse(map, Collections.emptyMap());
}

Ici, vous utiliseriez Objects::requireNonNullElse, qui renvoie l'objet passé dans le premier paramètre s'il ne s'agit pas de null, et l'objet passé en tant que second paramètre si le premier paramètre est null. L'utilisation d'une méthode distincte permet de transmettre une référence de méthode à Stream::map, mais vous devez d'abord mapper les instances User sur leurs données Map


Deuxième option: Objects::requireNonNullElse en ligne

List<Map<?, ?> bar() {
    //...

    return users.stream()
                .filter(user -> id.equals(user.getId()))
                .map(User::getData)
                .map(map -> Objects.requireNonNullElse(map, Collections.emptyMap()))
                .collect(Collectors.toList());
}

Si vous ne souhaitez pas qu'une méthode distincte effectue uniquement cette tâche, vous pouvez la mettre en ligne et éventuellement même supprimer le premier mappage en faveur de .map(user -> Objects.requireNonNullElse(user.getData(), Collections.emptyMap())), mais je vous déconseille cela. N'ayez pas peur d'avoir plusieurs appels à Stream::map si cela rend le code plus lisible.


Conclusion

Je voudrais préférer la première option} car cela rend le code très lisible: vous savez que vous mappez les instances User aux données, puis vous rendez ces données nulles et sûres. 

La deuxième option est correcte, mais souffre d’une très longue ligne qui pourrait prêter à confusion au premier abord. C'est beaucoup mieux que d'avoir un lambda multiligne. J'éviterais à tout prix les lambdas multilignes et en extrairais le contenu dans une méthode séparée.

Une chose que vous pourriez être en mesure d’améliorer est le nom de la méthode nullSafeMap, afin d’éviter toute confusion entre Stream::map et Java.util.Map

Notez que vous n'avez pas besoin d'utiliser Objects::requireNonNullElseGet puisque Collections::emptyMap est une méthode légère qui lance et renvoie uniquement une constante:

public static final <K,V> Map<K,V> emptyMap() {
    return (Map<K,V>) EMPTY_MAP;
}

Objects::requireNonNullElseGet est créé pour les objets par défaut dont l'extraction ou la création est lourde.

1
Marv

Si vous avez déjà Apache Collections 4 en tant que dépendance:

return users
    .stream()
    .filter(user -> id.equals(user.getId()))
    .map(User::getData)
    .map(MapUtils::emptyIfNull)
    .collect(Collectors.toList())
;

Si vous n'utilisez pas les collections Apache, définissez simplement une méthode d'assistance:

public static <K,V> Map<K,V> emptyIfNull(Map<K,V> map) {
    return map == null ? Collections.<K,V>emptyMap() : map;
}
0
Illya Kysil