Introduction aux flux Java 8

1. Vue d'ensemble

Dans cet article, nous allons jeter un coup d'œil à l'une des principales nouvelles fonctionnalités ajoutées par Java 8 - Streams.

Nous expliquerons en quoi consistent les flux et présenterons la création et les opérations de base des flux avec des exemples simples.

2. API Stream

L'une des nouvelles fonctionnalités majeures de Java 8 est l'introduction de la fonctionnalité de flux - java.util.stream - qui contient des classes pour traiter des séquences d'éléments.

La classe d'API centrale est le Stream. La section suivante montre comment créer des flux à l'aide des sources de fournisseur de données existantes.

2.1. Création de flux

Les flux peuvent être créés à partir de différentes sources d'éléments, par exemple une collection ou un tableau à l'aide des méthodes stream () et of () :

String[] arr = new String[]{"a", "b", "c"}; Stream stream = Arrays.stream(arr); stream = Stream.of("a", "b", "c");

Une méthode par défaut stream () est ajoutée à l' interface Collection et permet de créer un Stream en utilisant n'importe quelle collection comme source d'élément :

Stream stream = list.stream(); 

2.2. Multi-threading avec des flux

L'API Stream simplifie également le multithreading en fournissant la méthode parallelStream () qui exécute des opérations sur les éléments de stream en mode parallèle.

Le code ci-dessous permet d'exécuter la méthode doWork () en parallèle pour chaque élément du flux:

list.parallelStream().forEach(element -> doWork(element));

Dans la section suivante, nous présenterons certaines des opérations de base de l'API Stream.

3. Opérations de flux

Il existe de nombreuses opérations utiles qui peuvent être effectuées sur un flux.

Elles sont divisées en opérations intermédiaires (retour Stream ) et opérations terminales (retour d'un résultat de type défini). Les opérations intermédiaires permettent le chaînage.

Il convient également de noter que les opérations sur les flux ne changent pas la source.

Voici un exemple rapide:

long count = list.stream().distinct().count();

Ainsi, la méthode distinct () représente une opération intermédiaire, qui crée un nouveau flux d'éléments uniques du flux précédent. Et la méthode count () est une opération de terminal , qui renvoie la taille du flux.

3.1. Itérer

API flux aide à se substituer à , pour chaque- et en boucles. Il permet de se concentrer sur la logique de l'opération, mais pas sur l'itération sur la séquence d'éléments. Par exemple:

for (String string : list) { if (string.contains("a")) { return true; } }

Ce code peut être modifié avec une seule ligne de code Java 8:

boolean isExist = list.stream().anyMatch(element -> element.contains("a"));

3.2. Filtration

La méthode filter () nous permet de choisir un flux d'éléments qui satisfont un prédicat.

Par exemple, considérez la liste suivante:

ArrayList list = new ArrayList(); list.add("One"); list.add("OneAndOnly"); list.add("Derek"); list.add("Change"); list.add("factory"); list.add("justBefore"); list.add("Italy"); list.add("Italy"); list.add("Thursday"); list.add(""); list.add("");

Le code suivant crée un flux de la liste , recherche tous les éléments de ce flux qui contiennent le caractère «d» et crée un nouveau flux contenant uniquement les éléments filtrés:

Stream stream = list.stream().filter(element -> element.contains("d"));

3.3. Cartographie

Pour convertir des éléments d'un Stream en leur appliquant une fonction spéciale et pour collecter ces nouveaux éléments dans un Stream , nous pouvons utiliser la méthode map () :

List uris = new ArrayList(); uris.add("C:\\My.txt"); Stream stream = uris.stream().map(uri -> Paths.get(uri));

Ainsi, le code ci-dessus convertit Stream en Stream en appliquant une expression lambda spécifique à chaque élément du Stream initial .

Si vous avez un flux dans lequel chaque élément contient sa propre séquence d'éléments et que vous souhaitez créer un flux de ces éléments internes, vous devez utiliser la méthode flatMap () :

List details = new ArrayList(); details.add(new Detail()); Stream stream = details.stream().flatMap(detail -> detail.getParts().stream());

Dans cet exemple, nous avons une liste d'éléments de type Détail . La classe Detail contient un champ PARTS , qui est une liste . Avec l'aide de la méthode flatMap () , chaque élément du champ PARTS sera extrait et ajouté au nouveau flux résultant. Après cela, le Stream initial sera perdu .

3.4. Correspondant à

L'API Stream fournit un ensemble d'instruments pratiques pour valider les éléments d'une séquence en fonction d'un prédicat. Pour ce faire, l'une des méthodes suivantes peut être utilisée: anyMatch (), allMatch (), noneMatch (). Leurs noms sont explicites. Ce sont des opérations de terminal qui renvoient un booléen :

boolean isValid = list.stream().anyMatch(element -> element.contains("h")); // true boolean isValidOne = list.stream().allMatch(element -> element.contains("h")); // false boolean isValidTwo = list.stream().noneMatch(element -> element.contains("h")); // false

For empty streams, the allMatch() method with any given predicate will return true:

Stream.empty().allMatch(Objects::nonNull); // true

This is a sensible default, as we can't find any element that doesn't satisfy the predicate.

Similarly, the anyMatch() method always returns false for empty streams:

Stream.empty().anyMatch(Objects::nonNull); // false

Again, this is reasonable, as we can't find an element satisfying this condition.

3.5. Reduction

Stream API allows reducing a sequence of elements to some value according to a specified function with the help of the reduce() method of the type Stream. This method takes two parameters: first – start value, second – an accumulator function.

Imagine that you have a List and you want to have a sum of all these elements and some initial Integer (in this example 23). So, you can run the following code and result will be 26 (23 + 1 + 1 + 1).

List integers = Arrays.asList(1, 1, 1); Integer reduced = integers.stream().reduce(23, (a, b) -> a + b);

3.6. Collecting

The reduction can also be provided by the collect() method of type Stream. This operation is very handy in case of converting a stream to a Collection or a Map and representing a stream in the form of a single string. There is a utility class Collectors which provide a solution for almost all typical collecting operations. For some, not trivial tasks, a custom Collector can be created.

List resultList = list.stream().map(element -> element.toUpperCase()).collect(Collectors.toList());

Ce code utilise l'opération terminal collect () pour réduire un Stream dans la liste.

4. Conclusions

Dans cet article, nous avons brièvement abordé les flux Java - certainement l'une des fonctionnalités les plus intéressantes de Java 8.

Il existe de nombreux exemples plus avancés d'utilisation de Streams; L'objectif de cet article était uniquement de fournir une introduction rapide et pratique à ce que vous pouvez commencer à faire avec la fonctionnalité et comme point de départ pour explorer et approfondir votre apprentissage.

Le code source accompagnant l'article est disponible à l'adresse over sur GitHub.