JavaRush /Blog Java /Random-FR /Pause café #177. Un guide détaillé de Java Stream dans Ja...

Pause café #177. Un guide détaillé de Java Stream dans Java 8

Publié dans le groupe Random-FR
Source : Hackernoon Cet article fournit un didacticiel détaillé sur l'utilisation de Java Stream ainsi que des exemples de code et des explications. Pause café #177.  Un guide détaillé de Java Stream dans Java 8 - 1

Introduction aux threads Java dans Java 8

Les flux Java, introduits dans Java 8, sont utilisés pour travailler avec des collections de données. Ils ne constituent pas en eux-mêmes une structure de données, mais peuvent être utilisés pour saisir des informations à partir d'autres structures de données en les ordonnant et en les pipelinant pour produire un résultat final. Remarque : Il est important de ne pas confondre Stream et Thread, car en russe, les deux termes sont souvent désignés dans la même traduction « flux ». Stream désigne un objet permettant d'effectuer des opérations (le plus souvent le transfert ou le stockage de données), tandis que Thread (traduction littérale - thread) désigne un objet qui permet à certains codes de programme d'être exécutés en parallèle avec d'autres branches de code. Étant donné que Stream n'est pas une structure de données distincte, il ne modifie jamais la source de données. Les flux Java ont les fonctionnalités suivantes :
  1. Java Stream peut être utilisé à l'aide du package « java.util.stream ». Il peut être importé dans un script à l'aide du code :

    import java.util.stream.* ;

    En utilisant ce code, nous pouvons également facilement implémenter plusieurs fonctions intégrées dans Java Stream.

  2. Java Stream peut accepter les entrées de collections de données telles que des collections et des tableaux en Java.

  3. Java Stream ne nécessite pas de modification de la structure des données d'entrée.

  4. Java Stream ne change pas la source. Au lieu de cela, il génère une sortie à l’aide des méthodes de pipeline appropriées.

  5. Les flux Java sont soumis à des opérations intermédiaires et terminales, dont nous parlerons dans les sections suivantes.

  6. Dans Java Stream, les opérations intermédiaires sont pipeline et se produisent dans un format d'évaluation différée. Ils se terminent par des fonctions de terminal. Il s'agit du format de base pour l'utilisation de Java Stream.

Dans la section suivante, nous examinerons les différentes méthodes utilisées dans Java 8 pour créer un flux Java selon les besoins.

Création d'un flux Java dans Java 8

Les threads Java peuvent être créés de plusieurs manières :

1. Création d'un flux vide à l'aide de la méthode Stream.empty()

Vous pouvez créer un flux vide pour une utilisation ultérieure dans votre code. Si vous utilisez la méthode Stream.empty() , un flux vide sera généré, ne contenant aucune valeur. Ce flux vide peut s'avérer utile si nous souhaitons ignorer une exception de pointeur nul au moment de l'exécution. Pour ce faire, vous pouvez utiliser la commande suivante :
Stream<String> str = Stream.empty();
L'instruction ci-dessus générera un flux vide nommé str sans aucun élément à l'intérieur. Pour le vérifier, vérifiez simplement le nombre ou la taille du flux en utilisant le terme str.count() . Par exemple,
System.out.println(str.count());
En conséquence, nous obtenons 0 en sortie .

2. Créez un flux à l'aide de la méthode Stream.builder() avec une instance Stream.Builder

Nous pouvons également utiliser Stream Builder pour créer un flux à l'aide du modèle de conception du générateur. Il est conçu pour la construction d'objets étape par étape. Voyons comment instancier un flux à l'aide de Stream Builder .
Stream.Builder<Integer> numBuilder = Stream.builder();

numBuilder.add(1).add(2).add( 3);

Stream<Integer> numStream = numBuilder.build();
En utilisant ce code, vous pouvez créer un flux nommé numStream contenant des éléments int . Tout se fait assez rapidement grâce à l' instance Stream.Builder appelée numBuilder qui est créée en premier.

3. Créez un flux avec les valeurs spécifiées à l'aide de la méthode Stream.of()

Une autre façon de créer un flux consiste à utiliser la méthode Stream.of() . C'est un moyen simple de créer un flux avec des valeurs données. Il déclare et initialise également le thread. Un exemple d'utilisation de la méthode Stream.of() pour créer un flux :
Stream<Integer> numStream = Stream.of(1, 2, 3);
Ce code créera un flux contenant des éléments int , tout comme nous l'avons fait dans la méthode précédente en utilisant Stream.Builder . Ici, nous avons directement créé un flux en utilisant Stream.of() avec des valeurs prédéfinies [1, 2 et 3] .

4. Créez un flux à partir d'un tableau existant à l'aide de la méthode Arrays.stream()

Une autre méthode courante pour créer un thread consiste à utiliser des tableaux en Java. Le flux ici est créé à partir d'un tableau existant à l'aide de la méthode Arrays.stream() . Tous les éléments du tableau sont convertis en éléments de flux. Voici un bon exemple :
Integer[] arr = {1, 2, 3, 4, 5};

Stream<Integer> numStream = Arrays.stream(arr);
Ce code générera un numStream contenant le contenu d'un tableau appelé arr, qui est un tableau d'entiers.

5. Fusion de deux flux existants à l'aide de la méthode Stream.concat()

Une autre méthode qui peut être utilisée pour créer un flux est la méthode Stream.concat() . Il est utilisé pour combiner deux threads pour créer un seul thread. Les deux flux sont combinés dans l’ordre. Cela signifie que le premier thread vient en premier, suivi du deuxième thread, et ainsi de suite. Un exemple d'une telle concaténation ressemble à ceci :
Stream<Integer> numStream1 = Stream.of(1, 2, 3, 4, 5);

Stream<Integer> numStream2 = Stream.of(1, 2, 3);

Stream<Integer> combinedStream = Stream.concat( numStream1, numStream2);
L'instruction ci-dessus créera un flux final nommé CombinedStream contenant les éléments du premier flux numStream1 et du deuxième flux numStream2 un par un .

Types d'opérations avec Java Stream

Comme déjà mentionné, vous pouvez effectuer deux types d'opérations avec Java Stream dans Java 8 : intermédiaire et terminal. Examinons chacun d'eux plus en détail.

Opérations intermédiaires

Les opérations intermédiaires génèrent un flux de sortie et sont exécutées uniquement lorsqu'elles rencontrent une opération de terminal. Cela signifie que les opérations intermédiaires sont exécutées paresseusement, en pipeline et ne peuvent être complétées que par une opération de terminal. Vous en apprendrez davantage sur l'évaluation paresseuse et le pipeline un peu plus tard. Des exemples d'opérations intermédiaires sont les méthodes suivantes : filter() , map() , different() , peek() , sorted() et quelques autres.

Opérations des terminaux

Les opérations de terminal complètent l'exécution des opérations intermédiaires et renvoient également les résultats finaux du flux de sortie. Étant donné que les opérations de terminal signalent la fin de l’exécution paresseuse et du pipeline, ce thread ne peut pas être réutilisé après avoir subi une opération de terminal. Des exemples d'opérations de terminal sont les méthodes suivantes : forEach() , collect() , count() , reduction() et ainsi de suite.

Exemples d'opérations avec Java Stream

Opérations intermédiaires

Voici quelques exemples d'opérations intermédiaires pouvant être appliquées à un flux Java :

filtre()

Cette méthode est utilisée pour filtrer les éléments d'un flux qui correspondent à un prédicat spécifique en Java. Ces éléments filtrés constituent alors un nouveau flux. Jetons un coup d'œil à un exemple pour mieux comprendre.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> even = numStream.filter(n -> n % 2 == 0) .collect(Collectors.toList()); System.out.println(even);
Conclusion:
[98]
Explication : Dans cet exemple, vous pouvez voir que même les éléments (divisibles par 2) sont filtrés à l'aide de la méthode filter() et stockés dans une liste d'entiers numStream , dont le contenu est imprimé ultérieurement. Puisque 98 est le seul entier pair du flux, il est imprimé dans la sortie.

carte()

Cette méthode est utilisée pour créer un nouveau flux en exécutant des fonctions mappées sur des éléments du flux d'entrée d'origine. Peut-être que le nouveau flux a un type de données différent. L'exemple ressemble à ceci :
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> d = numStream.map(n -> n*2) .collect(Collectors.toList()); System.out.println(d);
Conclusion:
[86, 130, 2, 196, 126]
Explication : Nous voyons ici que la méthode map() est utilisée pour simplement doubler chaque élément du flux numStream . Comme vous pouvez le voir sur le résultat, chacun des éléments du flux a été doublé avec succès.

distinct()

Cette méthode est utilisée pour récupérer uniquement les éléments individuels d'un flux en filtrant les doublons. Un exemple similaire ressemble à ceci :
Stream<Integer> numStream = Stream.of(43,65,1,98,63,63,1); List<Integer> numList = numStream.distinct() .collect(Collectors.toList()); System.out.println(numList);
Conclusion:
[43, 65, 1, 98, 63]
Explication : Dans ce cas, la méthode different() est utilisée pour numStream . Il récupère tous les éléments individuels de numList en supprimant les doublons du flux. Comme vous pouvez le voir sur la sortie, il n'y a pas de doublons, contrairement au flux d'entrée, qui avait initialement deux doublons (63 et 1).

coup d'oeil()

Cette méthode est utilisée pour suivre les modifications intermédiaires avant d'exécuter une opération de terminal. Cela signifie que peek() peut être utilisé pour effectuer une opération sur chaque élément d'un flux afin de créer un flux sur lequel d'autres opérations intermédiaires peuvent être effectuées.
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> nList = numStream.map(n -> n*10) .peek(n->System.out.println("Mapped: "+ n)) .collect(Collectors.toList()); System.out.println(nList);
Conclusion:
Cartographié : 430 Cartographié : 650 Cartographié : 10 Cartographié : 980 Cartographié : 630 [430, 650, 10, 980, 630]
Explication : Ici, la méthode peek() est utilisée pour générer des résultats intermédiaires car la méthode map() est appliquée aux éléments du flux. Ici, nous pouvons remarquer qu'avant même d'utiliser l'opération de terminal collect() pour imprimer le contenu final de la liste dans l' instruction print , le résultat de chaque mappage d'élément de flux est imprimé séquentiellement à l'avance.

trié()

La méthode sorted() est utilisée pour trier les éléments d'un flux. Par défaut, il trie les éléments par ordre croissant. Vous pouvez également spécifier un ordre de tri spécifique en tant que paramètre. Un exemple d'implémentation de cette méthode ressemble à ceci :
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.sorted().forEach(n -> System.out.println(n));
Conclusion:
1 43 ​​63 65 98
Explication : Ici, la méthode sorted() est utilisée pour trier par défaut les éléments du flux par ordre croissant (puisqu'aucun ordre spécifique n'est spécifié). Vous pouvez voir que les éléments imprimés dans la sortie sont classés par ordre croissant.

Opérations des terminaux

Voici quelques exemples d'opérations de terminal pouvant être appliquées aux flux Java :

pour chaque()

La méthode forEach() est utilisée pour parcourir tous les éléments d'un flux et exécuter la fonction sur chaque élément un par un. Cela constitue une alternative aux instructions de boucle telles que for , while et autres. Exemple:
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); numStream.forEach(n -> System.out.println(n));
Conclusion:
43 65 1 98 63
Explication : Ici, la méthode forEach() est utilisée pour imprimer chaque élément du flux un par un.

compter()

La méthode count() permet de récupérer le nombre total d'éléments présents dans le flux. Elle est similaire à la méthode size() , qui est souvent utilisée pour déterminer le nombre total d'éléments dans une collection. Un exemple d'utilisation de la méthode count() avec un Java Stream est le suivant :
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); System.out.println(numStream.count());
Conclusion:
5
Explication : Puisque numStream contient 5 éléments entiers, l'utilisation de la méthode count() en produira 5.

collecter()

La méthode collect() est utilisée pour effectuer des réductions mutables des éléments du flux. Il peut être utilisé pour supprimer du contenu d’un flux une fois le traitement terminé. Il utilise la classe Collector pour effectuer des réductions .
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); List<Integer> odd = numStream.filter(n -> n % 2 == 1) .collect(Collectors.toList()); System.out.println(odd);
Conclusion:
[43, 65, 1, 63]
Explication : Dans cet exemple, tous les éléments impairs du flux sont filtrés et collectés/réduit dans une liste nommée odd . A la fin, une liste des impairs est imprimée.

min() et max()

La méthode min() , comme son nom l'indique, peut être utilisée sur un flux pour y trouver l'élément minimum. De même, la méthode max() peut être utilisée pour trouver le maximum d’éléments dans un flux. Essayons de comprendre comment ils peuvent être utilisés avec un exemple :
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); int smallest = numStream.min((m, n) -> Integer.compare(m, n)).get(); System.out.println("Smallest element: " + smallest);
numStream = Stream.of(43, 65, 1, 98, 63); int largest = numStream.max((m, n) -> Integer.compare(m, n)).get(); System.out.println("Largest element: " + largest);
Conclusion:
Plus petit élément : 1 Plus grand élément : 98
Explication : Dans cet exemple, nous avons imprimé le plus petit élément du numStream à l'aide de la méthode min() et le plus grand élément à l'aide de la méthode max() . Notez qu'ici, avant d'utiliser la méthode max() , nous avons à nouveau ajouté des éléments au numStream . En effet, min() est une opération de terminal et détruit le contenu du flux d'origine, ne renvoyant que le résultat final (qui dans ce cas était l'entier « le plus petit »).

findAny() et findFirst()

findAny() renvoie n'importe quel élément du flux comme Optionnel . Si le flux est vide, il renverra également une valeur facultative , qui sera vide. findFirst() renvoie le premier élément du flux sous la forme Facultatif . Comme pour la méthode findAny() , la méthode findFirst() renvoie également un paramètre facultatif vide si le flux correspondant est vide. Jetons un coup d'œil à l'exemple suivant basé sur ces méthodes :
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); Optional<Integer> opt = numStream.findFirst();System.out.println(opt); numStream = Stream.empty(); opt = numStream.findAny();System.out.println(opt);
Conclusion:
Facultatif[43] Facultatif.vide
Explication : Ici, dans le premier cas, la méthode findFirst() renvoie le premier élément du flux commeOptional . Ensuite, lorsque le thread est réaffecté en tant que thread vide, la méthode findAny() renvoie un Option vide .

allMatch() , anyMatch() et noneMatch()

La méthode allMatch() est utilisée pour vérifier si tous les éléments d'un flux correspondent à un certain prédicat et renvoie la valeur booléenne true si c'est le cas, sinon renvoie false . Si le flux est vide, il renvoie true . La méthode anyMatch() est utilisée pour vérifier si l'un des éléments d'un flux correspond à un certain prédicat. Il renvoie vrai si c'est le cas, faux sinon. Si le flux est vide, il renvoie false . La méthode noneMatch() renvoie true si aucun élément du flux ne correspond au prédicat, et false sinon. Un exemple pour illustrer cela ressemble à ceci :
Stream<Integer> numStream = Stream.of(43, 65, 1, 98, 63); boolean flag = numStream.allMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.anyMatch(n -> n1); System.out.println(flag); numStream = Stream.of(43, 65, 1, 98, 63); flag = numStream.noneMatch(n -> n==1);System.out.println(flag);
Conclusion:
faux vrai faux
Explication : Pour un flux numStream contenant 1 comme élément, la méthode allMatch() renvoie false car tous les éléments ne sont pas 1, mais un seul d'entre eux l'est. La méthode anyMatch() renvoie true car au moins un des éléments est 1. La méthode noneMatch() renvoie false car 1 existe réellement en tant qu'élément dans ce flux.

Évaluations paresseuses dans Java Stream

L'évaluation paresseuse conduit à des optimisations lors de l'utilisation de Java Streams dans Java 8. Elles impliquent principalement de retarder les opérations intermédiaires jusqu'à ce qu'une opération de terminal soit rencontrée. L'évaluation paresseuse est chargée d'éviter un gaspillage inutile de ressources lors des calculs jusqu'à ce que le résultat soit réellement nécessaire. Le flux de sortie résultant des opérations intermédiaires n'est généré qu'une fois l'opération du terminal terminée. L'évaluation paresseuse fonctionne avec toutes les opérations intermédiaires dans les flux Java. Une utilisation très utile de l'évaluation paresseuse se produit lorsque vous travaillez avec des flux infinis. De cette façon, de nombreux traitements inutiles sont évités.

Pipelines dans le flux Java

Un pipeline dans un flux Java se compose d'un flux d'entrée, de zéro ou plusieurs opérations intermédiaires alignées les unes après les autres, et enfin d'une opération terminale. Les opérations intermédiaires dans Java Streams sont effectuées paresseusement. Cela rend inévitables les opérations intermédiaires en pipeline. Avec les pipelines, qui sont essentiellement des opérations intermédiaires combinées dans l’ordre, une exécution paresseuse devient possible. Les pipelines aident à garder une trace des opérations intermédiaires qui doivent être effectuées une fois qu'une opération de terminal est finalement rencontrée.

Conclusion

Résumons maintenant ce que nous avons appris aujourd’hui. Dans cet article:
  1. Nous avons jeté un rapide coup d’œil à ce que sont les flux Java.
  2. Nous avons ensuite appris de nombreuses techniques différentes pour créer des threads Java dans Java 8.
  3. Nous avons appris deux principaux types d'opérations (opérations intermédiaires et opérations de terminal) pouvant être effectuées sur les flux Java.
  4. Nous avons ensuite examiné en détail plusieurs exemples d’opérations intermédiaires et terminales.
  5. Nous avons fini par en apprendre davantage sur l'évaluation paresseuse et enfin sur le pipeline dans les threads Java.
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION