Est-il sensé de retourner des Streams partout où nous retournerions normalement des Collections?

19

Tout en développant mon API qui n'est liée à aucun code hérité, je me retrouve souvent à écrire des méthodes qui sont purement et simplement terminées par le pipeline Streams en collectant les résultats. Comme celui-ci:

ImmutableSet<T> deriveSomethingMeaningfulFromPrivateState() {
    return myPrivateThingies.stream()
        .map(this::ownerOfThing)
        .map(Owner::socialStatus)
        .filter(SocialStatus::isHeAFineMatey)
        .collect(MyCustomCollectors.toImmutableSet());
}

Maintenant, la plupart des clients de cette classe auront généralement besoin de la collection (dans ce cas, un ensemble immuable) pour rechercher des éléments et les itérer, mais certains clients peuvent bénéficier d'un flux afin de pouvoir diriger plus d'opérations en plus de cela. Diffusez sans avoir besoin d'obtenir un nouveau flux de la collection. Donc, renvoyer un Stream donne aux cliens un sur-ensemble d'options qu'ils auraient s'ils avaient juste la Collection (après tout, ils peuvent toujours collect()le Stream eux-mêmes:

Stream<T> deriveSomethingMeaningfulFromPrivateState() {
    return myPrivateThingies.stream()
        .map(this::ownerOfthing)
        .map(Owner::socialStatus)
        .filter(SocialStatus::isHeAFineMatey);
        // No collect
}

Cette approche est tentante pour moi d'essayer car je ne vois pas de défauts potentiels qu'elle pourrait avoir. Cependant, je n'ai jamais vu cette approche dans aucune bibliothèque (probablement parce qu'il n'y avait pas beaucoup de bibliothèques publiées après l'apparition de Java 8), j'ai donc un peu peur de l'adopter. Les classes de bibliothèque existantes renvoient généralement des collections lorsqu'elles dérivent quelque chose de l'état privé.

Y a-t-il quelque chose de mal qui pourrait se produire si je décidais de retourner un Stream partout où mon moi pré-Java-8 retournerait une Collection? Ou est-ce que je fais ici quelque chose d'anti-modèle avec tout ce qui dérive de l'état privé?

jojman
la source

Réponses:

14

Si myPrivateThingiesest mutable, vous avez créé une dépendance cachée entre votre état privé et les résultats du flux. S'il est possible pour le client de provoquer indirectement un myPrivateThingieschangement d'état, il obtiendra un résultat différent lors de l'appel collectque celui que vous aviez l'intention de donner à l'origine.

Si myPrivateThingiesest immuable, alors le résultat sera référentiellement transparent, mais il y a un autre problème auquel vous devez faire attention: les ordures sémantiques , c'est-à-dire conserver de grandes quantités de mémoire qui ne sont plus nécessaires. Supposons qu'il myPrivateThingiessoit très grand et que le résultat de la collecte du flux soit petit. Le client peut conserver le flux longtemps après avoir jeté toutes les références à l'objet qui l'a produit, mais cela streamne l'empêche pas myPrivateThingiesd'être récupéré. La collecte avide des résultats permettrait myPrivateThingiesde se libérer.

Cela s'est réellement produit avant Java 7 lors de l'appel substring. Oracle a décidé que les économies potentielles d'efficacité en ne copiant pas la sous-chaîne à chaque fois ne valaient pas la peine de surprendre parfois l'utilisateur moyen avec une consommation de mémoire excessive. Cela ne veut pas dire qu'il n'y avait pas de cas d'utilisation réels pour l'ancien comportement (par exemple, les analyseurs), mais souvent la collecte des résultats avec impatience est assez rapide, et lorsque cela se produit, vous n'avez pas d'avantages et de con potentiels.

D'un autre côté, le retour d'un flux donne au client la possibilité de choisir la structure de données qu'il souhaite utiliser pour conserver les résultats, au lieu que vous en choisissiez un pour lui. Il peut être intéressant d'offrir les deux options.

Doval
la source
4

La chose la plus importante à considérer: les Streams ne peuvent être itérés qu'une seule fois, alors que vous avez plus de flexibilité sur a Collection: vous pouvez continuer à créer plus de Streams ou même des Iterators pour effectuer un traitement répétitif supplémentaire sur les résultats.

Donc, si vous n'êtes pas sûr que les appelants de la méthode utiliseront les résultats une seule fois, il est préférable de renvoyer a Collection.


Votre exemple de code comporte une erreur évidente: pourquoi un SocialStatusconcept aurait-il une personne he?

hjk
la source
3

À mon avis, non. Les choses que vous pouvez faire avec les flux sont un sur-ensemble strict des choses que vous pouvez faire avec les collections, et souvent elles peuvent être rendues plus efficaces, il n'y a donc aucune raison de ne pas les utiliser, sauf par méconnaissance. "Les expressions lambda sont le médicament de passerelle vers Java 8, mais les Streams sont la véritable dépendance." (Venkat Subramaniam, Programmation fonctionnelle en Java )

Kilian Foth
la source