web-dev-qa-db-fra.com

En Java, quels sont les avantages des flux sur les boucles?

On m'a posé cette question lors d'une interview et je ne suis pas convaincue d'avoir donné la meilleure réponse possible. J'ai mentionné que vous pouviez faire une recherche parallèle et que les valeurs nulles étaient gérées par un moyen dont je ne me souvenais pas. Maintenant, je réalise que je pensais à Optionals. Qu'est-ce que j'oublie ici? Ils prétendent que c'est un code meilleur ou plus concis, mais je ne suis pas sûr d'être d'accord.


Compte tenu de la précision avec laquelle la réponse a été donnée, il semble que la question ne soit pas trop vaste après tout.


S'ils posent cette question lors des entretiens, et ils le sont clairement, à quoi servirait-il de le décomposer autrement que de rendre plus difficile la recherche d'une réponse? Je veux dire, que cherches-tu? Je pourrais décomposer la question et obtenir une réponse à toutes les sous-questions, puis créer une question parent avec des liens vers toutes les sous-questions. Cela semble assez stupide. Pendant que nous y sommes, donnez-moi s'il vous plaît un exemple d'une question moins large. Je ne connais aucun moyen de ne poser qu'une partie de cette question et d'obtenir une réponse significative. Je pourrais poser exactement la même question d'une manière différente. Par exemple, je pourrais demander "A quoi servent les flux?" ou "Quand utiliserais-je un flux au lieu d'une boucle for?" ou "Pourquoi s'embêter avec des flux plutôt que pour des boucles?" Ce sont tous exactement la même question cependant.

... ou est-il considéré comme trop large parce que quelqu'un a donné une réponse très longue en plusieurs points? Franchement, tout le monde peut le faire avec pratiquement toutes les questions. Si vous êtes l’un des auteurs de la machine virtuelle Java, par exemple, vous pourriez probablement parler de boucles for tout au long de la journée alors que la plupart d’entre nous ne le pourraient pas.

"Veuillez modifier la question pour la limiter à un problème spécifique avec suffisamment de détails pour identifier une réponse adéquate. Évitez de poser plusieurs questions distinctes à la fois. Voir la page Comment poser une question pour obtenir de l'aide pour clarifier cette question."

Comme indiqué ci-dessous, une réponse adéquate a été donnée qui prouve qu’il en existe une et qu’elle est assez facile à fournir.

102
user447607

Il est intéressant de noter que la question de l’entrevue porte sur les avantages, sans poser de questions sur les inconvénients, car il existe les deux.

Les flux sont un style plus déclaratif. Ou un style plus expressif. Il peut être préférable de déclarer votre intention dans le code plutôt que de décrire comment c'est fait:

 return people
     .filter( p -> p.age() < 19)
     .collect(toList());

... dit très clairement que vous filtrez les éléments correspondants d'une liste, alors que:

 List<Person> filtered = new ArrayList<>();
 for(Person p : people) {
     if(p.age() < 19) {
         filtered.add(p);
     }
 }
 return filtered;

Dit "je fais une boucle". Le but de la boucle est enfoui plus profondément dans la logique.

Les flux sont souvent terser. Le même exemple montre cela. Terser n'est pas toujours meilleur, mais si vous pouvez être concis et expressif en même temps, tant mieux.

Les flux ont une forte affinité avec les fonctions. Java 8 introduit les langdas et les interfaces fonctionnelles, ce qui ouvre toute une boîte à jouets de techniques puissantes. Les flux constituent le moyen le plus pratique et naturel d'appliquer des fonctions à des séquences d'objets.

Les flux encouragent moins de mutabilité. Ceci est en quelque sorte lié à l'aspect de la programmation fonctionnelle - le genre de programmes que vous écrivez en utilisant des flux de données ont tendance à être le genre de programmes où vous ne modifiez pas les objets.

Les flux encouragent un couplage plus lâche. Votre code de gestion de flux n'a pas besoin de connaître la source du flux ni sa méthode de terminaison éventuelle.

Les flux peuvent exprimer succinctement un comportement assez sophistiqué. Par exemple:

 stream.filter(myfilter).findFirst();

À première vue, cela pourrait filtrer le flux entier, puis renvoyer le premier élément. Mais en fait, findFirst() gère l'ensemble de l'opération, de sorte qu'elle s'arrête efficacement après la recherche d'un élément.

Les flux permettent des gains d’efficacité futurs. Certaines personnes ont analysé et constaté que les flux mono-threadés provenant de Lists ou de tableaux en mémoire peuvent être plus lents que la boucle équivalente. Ceci est plausible car il y a plus d'objets et de frais généraux en jeu.

Mais les cours d'eau sont à l'échelle. Outre la prise en charge intégrée de Java pour les opérations de flux parallèles, il existe quelques bibliothèques pour réduire les cartes distribuées en utilisant Streams comme API, car le modèle convient.

Inconvénients?

Performance: Une boucle for à travers un tableau est extrêmement légère en termes d'utilisation du tas et du processeur. Si la vitesse brute et l’économie de mémoire sont une priorité, l’utilisation d’un flux est pire.

Familiarity. Le monde regorge de programmeurs procéduraux expérimentés, issus de nombreux langages linguistiques, pour lesquels les boucles sont familières et les flux sont nouveaux. Dans certains environnements, vous souhaitez écrire un code familier à ce type de personne.

surcharge cognitive. En raison de sa nature déclarative et de l'abstraction accrue de ce qui se passe en-dessous, vous devrez peut-être créer un nouveau modèle mental du lien entre code et exécution. En réalité, vous ne devez le faire que lorsque les choses tournent mal ou si vous devez analyser en profondeur les performances ou les bugs subtils. Quand cela "fonctionne", cela fonctionne.

Les débogueurs s'améliorent, mais même maintenant, lorsque vous parcourez le code de flux dans un débogueur, cela peut être plus dur que la boucle équivalente, car une simple boucle est très proche des variables et des emplacements de code qu'un débogueur traditionnel fonctionne avec.

199
slim

Mis à part le plaisir syntaxique, les flux sont conçus pour fonctionner avec des ensembles de données potentiellement infiniment volumineux, alors que les tableaux, les collections et presque toutes les classes Java SE qui implémentent Iterable sont entièrement en mémoire.

Un des inconvénients d'un flux est que les filtres, les mappages, etc., ne peuvent pas générer d'exceptions vérifiées. Cela fait d'un flux un mauvais choix pour, par exemple, les opérations d'E/S intermédiaires.

12
VGR
  1. Vous avez mal compris: les opérations parallèles utilisent Streams, et non Optionals.

  2. Vous pouvez définir des méthodes fonctionnant avec des flux: en les prenant comme paramètres, en les renvoyant, etc. Vous ne pouvez pas définir une méthode qui prend une boucle en paramètre. Cela permet une opération de flux compliquée une fois et l’utilise plusieurs fois. Notez que Java a un inconvénient ici: vos méthodes doivent être appelées someMethod(stream) par opposition aux propres stream.someMethod() du flux, aussi leur mélange complique la lecture: essayez de voir l'ordre des opérations dans

    myMethod2(myMethod(stream.transform(...)).filter(...))
    

    Beaucoup d'autres langages (C #, Kotlin, Scala, etc.) autorisent une forme de "méthodes d'extension".

  3. Même lorsque vous n'avez besoin que d'opérations séquentielles et que vous ne voulez pas les réutiliser, de sorte que vous puissiez utiliser des flux ou des boucles, des opérations simples sur les flux peuvent correspondre à des modifications assez complexes des boucles.

8
Alexey Romanov

Vous passez en boucle sur une séquence (tableau, collection, entrée, ...) parce que vous souhaitez appliquer une fonction aux éléments de la séquence.

Les flux vous permettent de composer des fonctions sur des éléments de séquence et de permettre de mettre en œuvre fonctions les plus courantes (p. ex. mappage, filtrage, recherche, tri, collecte, ...) indépendamment d’un cas concret.

Par conséquent, dans la plupart des cas, avec une tâche en boucle, vous pouvez l’exprimer avec moins de code en utilisant Streams, c’est-à-dire que vous gagnez en lisibilité .

4
wero

Je dirais que sa parallélisation qui est si facile à utiliser. Essayez d'itérer des millions d'entrées en parallèle avec une boucle for. Nous allons à beaucoup de processeurs, pas plus vite; donc plus il est facile de courir en parallèle mieux c'est, et avec Streams c'est un jeu d'enfant.

Ce que j’aime le plus, c’est le verbosité qu’ils offrent. Il faut peu de temps pour comprendre ce qu’ils font réellement et produire, par opposition à comment ils le font .

2
Eugene