157 votes

En Java, quels sont les avantages des flux par rapport aux boucles ?

On m'a posé cette question lors d'un entretien et je ne suis pas convaincu d'avoir donné la meilleure réponse possible. J'ai mentionné que vous pouviez effectuer une recherche parallèle et que les valeurs nulles étaient traitées par un moyen dont je ne me souvenais plus. Je réalise maintenant que je pensais aux optionnels. Qu'est-ce que j'ai manqué ici ? Ils prétendent que le code est meilleur ou plus concis, mais je ne suis pas sûr d'être d'accord.


Vu la brièveté de la réponse, il semble que la question n'était pas trop vaste après tout.


S'ils posent cette question lors des entretiens, et c'est manifestement le cas, à quoi peut bien servir de la décomposer, si ce n'est à rendre la réponse plus difficile à trouver ? Je veux dire, qu'est-ce que vous cherchez ? Je pourrais décomposer la question et répondre à toutes les sous-questions, mais ensuite créer une question mère avec des liens vers toutes les sous-questions... cela semble assez stupide cependant. Pendant que nous y sommes, donnez-moi un exemple de question moins large. Je ne connais aucun moyen de poser seulement 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 "À quoi servent les flux ?" ou "Quand utiliserais-je un flux au lieu d'une boucle for ?" ou "Pourquoi utiliser des flux au lieu de boucles for ?". Il s'agit pourtant exactement de la même question.

...ou est-ce considéré comme trop large parce que quelqu'un a donné une très longue réponse en plusieurs points ? Franchement, n'importe qui pourrait faire cela avec pratiquement n'importe quelle question. Si vous êtes l'un des auteurs de la JVM, par exemple, vous pourriez probablement parler de boucles for toute la journée alors que la plupart d'entre nous ne le pourraient pas.

"Veuillez éditer 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. Consultez la page Comment poser une question pour obtenir de l'aide afin de clarifier cette question."

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

10 votes

C'est une question d'opinion. Personnellement, je préfère les flux car ils rendent le code plus lisible. Il permet d'écrire ce que que vous voulez au lieu de comment . De plus, c'est vraiment génial de faire des choses incroyables avec des phrases toutes faites.

23 votes

Même si c'est une trentaine de lignes, une seule ? Je n'aime pas les longues chaînes.

2 votes

D'ailleurs, tout ce que je cherche ici, c'est la réponse appropriée pour un entretien. C'est la seule "opinion" qui compte.

288voto

slim Points 12620

Il est intéressant que la question de l'entretien porte sur les avantages, sans poser de questions sur les inconvénients, car il y a les deux.

Les flux sont un style plus déclaratif . Ou un plus expressif style. On peut considérer qu'il est préférable de déclarer son intention dans le code, plutôt que de décrire comment c'est fait :

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

... indique clairement qu'il s'agit de filtrer 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;

Ça dit "Je fais une boucle". Le but de la boucle est enfoui plus profondément dans la logique.

Les cours d'eau sont souvent terser . Le même exemple le montre. Plus laconique n'est pas toujours meilleur, mais si vous pouvez être laconique et expressif en même temps, c'est encore mieux.

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

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

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

Les flux peuvent exprimer succinctement des comportements assez sophistiqués. . Par exemple :

 stream.filter(myfilter).findFirst();

À première vue, on pourrait croire qu'il filtre l'ensemble du flux, puis renvoie le premier élément. Mais en fait findFirst() dirige l'ensemble de l'opération, de sorte qu'elle s'arrête efficacement après avoir trouvé un élément.

Les flux permettent des gains d'efficacité futurs . Certaines personnes ont effectué des analyses comparatives et ont constaté que les flux monofilaires provenant d'un système en mémoire List ou des tableaux peuvent être plus lents que la boucle équivalente. Cela est plausible car il y a plus d'objets et de frais généraux en jeu.

Mais les flux évoluent. Outre la prise en charge intégrée de Java pour les opérations de flux parallèles, il existe quelques bibliothèques pour le map-reduce distribué utilisant les flux comme API, car le modèle est adapté.

Inconvénients ?

Performance : A for boucler dans un tableau est extrêmement léger, tant en termes d'utilisation du tas que du CPU. Si la vitesse brute et l'économie de mémoire sont une priorité, l'utilisation d'un flux est pire.

Familiarité Le monde est rempli de programmeurs procéduraux expérimentés, issus de nombreux langages, pour qui les boucles sont familières et les flux nouveaux. Dans certains environnements, vous voulez écrire du code qui est familier à ce type de personne.

Frais généraux cognitifs . En raison de sa nature déclarative et de l'abstraction accrue de ce qui se passe en dessous, vous devrez peut-être construire un nouveau modèle mental de la relation entre le code et l'exécution. En fait, vous n'avez besoin de le faire que lorsque les choses vont mal, ou si vous devez analyser en profondeur les performances ou les bugs subtils. Quand ça "marche", ça marche.

Débogueurs s'améliorent, mais même aujourd'hui, lorsque vous parcourez le code d'un flux dans un débogueur, le travail peut être plus difficile que celui d'une boucle équivalente, car une simple boucle est très proche des variables et des emplacements de code avec lesquels un débogueur traditionnel travaille.

4 votes

Je pense qu'il serait juste de dire que les trucs de type stream deviennent beaucoup plus communs et apparaissent maintenant dans beaucoup de langages couramment utilisés qui ne sont pas spécialement orientés FP.

8 votes

Compte tenu des avantages et des inconvénients énumérés ici, je pense que les flux ne valent PAS la peine d'être utilisés autrement que pour des utilisations très simples (peu de logique if/then/else, peu d'appels imbriqués ou de lambdas, etc.), dans des domaines de performance non critiques.

4 votes

HenrikKjusAlstad Ce n'est absolument pas le message que je voulais transmettre. Les flux sont matures, puissants, expressifs et tout à fait appropriés pour le code de production.

21voto

VGR Points 4236

Au-delà de la syntaxe, les flux sont conçus pour fonctionner avec des ensembles de données potentiellement infiniment grands, alors que les tableaux, les collections et presque toutes les classes Java SE qui implémentent Iterable sont entièrement en mémoire.

L'inconvénient d'un flux est que les filtres, les mappings, etc. ne peuvent pas lancer d'exceptions vérifiées. Cela fait d'un flux un mauvais choix pour, par exemple, des opérations d'E/S intermédiaires.

10 votes

Bien sûr, vous pouvez aussi boucler sur des sources infinies.

0 votes

Mais si les éléments à traiter sont persistants dans une BD, comment utiliser les Streams ? Un développeur junior pourrait être tenté de les lire tous dans une Collection juste pour utiliser les Streams. Et ce serait un désastre.

3 votes

@LluisMartinez une bonne bibliothèque de client DB retournera quelque chose comme Stream<Row> -- ou il serait possible d'écrire sa propre Stream implémentation enveloppant les opérations du curseur de résultat de la DB.

8voto

Alexey Romanov Points 39124
  1. Vous avez mal compris : les opérations parallèles utilisent Stream et non Optional s.

  2. Vous pouvez définir des méthodes travaillant avec les flux : les prendre comme paramètres, les retourner, etc. Vous ne pouvez pas définir une méthode qui prend une boucle comme paramètre. Cela permet de réaliser une opération compliquée avec un flux une fois et de l'utiliser plusieurs fois. Notez que Java présente un inconvénient à cet égard : vos méthodes doivent être appelées en tant que someMethod(stream) par opposition à l'utilisation du flux stream.someMethod() Le fait de les mélanger complique donc la lecture : essayez de voir l'ordre des opérations dans le tableau suivant

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

    De nombreux autres langages (C#, Kotlin, Scala, etc.) autorisent une certaine 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 pourriez utiliser soit des flux, soit des boucles, les opérations simples sur les flux peuvent correspondre à des changements assez complexes dans les boucles.

0 votes

Expliquez 1. L'interface Optional n'est-elle pas le moyen par lequel les nuls sont traités dans les chaînes ? En ce qui concerne le point 3, c'est logique car avec les filtres court-circuités, la méthode ne sera invoquée que pour les occurrences spécifiées. Efficace. Il est logique que je puisse affirmer que leur utilisation réduit le besoin d'écrire du code supplémentaire qui devra être testé, etc. Après examen, je ne suis pas sûr de ce que vous voulez dire par le cas séquentiel en 2.

0 votes

1. Optional est une alternative à null mais cela n'a rien à voir avec les opérations parallèles. A moins que "Maintenant je réalise que je pensais aux optionnels" dans votre question ne parle uniquement de null la manipulation ?

0 votes

J'ai changé l'ordre des numéros 2 et 3 et je les ai un peu élargis.

7voto

wero Points 4576

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

Les flux vous donnent la possibilité de composer sur les éléments de la séquence et permettent de mettre en œuvre les fonctions les plus courantes (par exemple, cartographie, filtrage, recherche, tri, collecte, ...) indépendamment d'un cas concret.

Par conséquent, pour une tâche de bouclage, dans la plupart des cas, vous pouvez l'exprimer avec moins de code en utilisant les flux. lisibilité .

7 votes

Ce n'est pas seulement une question de lisibilité. Un code que vous n'avez pas à écrire est un code que vous n'avez pas à tester.

3 votes

Il semble que tu trouves de bonnes réponses pour ton entretien aussi.

6voto

Eugene Points 6271

Je dirais que c'est parallélisation qui est si facile à utiliser. Essayez d'itérer sur des millions d'entrées en parallèle avec une boucle for. Nous avons besoin de beaucoup de cpus, pas de plus de rapidité, donc plus il est facile de fonctionner en parallèle, mieux c'est. Stream c'est un jeu d'enfant.

Ce que j'aime beaucoup, c'est le verbosité qu'ils proposent. Il faut peu de temps pour comprendre ce qu'ils font et produisent réellement, contrairement à ce qui se passe dans les autres pays. comment ils le font.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X