[Java] Introduction à l'API Stream

Qu'est-ce que l'API Stream #?

C'est comme une suite de l'article ici.

Stream API est une API de traitement des données au format PipeLine. Extraire des éléments individuels d'une collection de données (source de données) comme une collection, un tableau, un fichier, etc. Il fournit un mécanisme pour le transmettre au "flux de processus" (Stream).

"Opération intermédiaire" qui renvoie le résultat de l'exécution d'une opération de fonction sur Stream en tant que Stream Il existe une "opération de terminaison" qui renvoie le résultat du traitement sous forme de données.

Étant donné que de nombreux arguments de méthode utilisent une interface fonctionnelle pour les opérations intermédiaires et de terminaison. Si la connaissance de style lambda est utilisée ici, ce sera intelligent. Stream API.png

Stream ...? Est-ce différent du flux d'E / S?

Java fournit un flux d'E / S dans le package java.io, La signification de Stream ici est un concept qui compare l'entrée et la sortie de Stream. Stream of Stream API est un concept qui fait du traitement PipeLine des données comme Stream.

Principes de base de l'API Stream

python


import java.util.ArrayList;
import java.util.Arrays;

public class Main {

    public static void main(String[] args) {

        //① Préparer la source de données
        var list = new ArrayList<String>(
        		Arrays.asList("tokyo", "nagoya", "osaka", "fukuoka", "hokkaido", "okinawa"));

        //② Créer un flux
        list.stream().
            filter(s -> s.length() > 5).         //③ Effectuer un traitement intermédiaire
            map(String::toUpperCase).
            forEach(System.out::println);        //④ Effectuer le traitement de résiliation

        //Résultat: NAGOYA FUKUOKA HOKKAIDO OKINAWA
    }
}

Le traitement de l'API Stream comprend les éléments suivants. ** ① Préparer la source de données → ② Génération de flux → ③ Traitement intermédiaire tel que l'extraction / traitement → ④ Traitement de terminaison tel que sortie / agrégation **

Dans l'exemple ci-dessus, ① Commencez par créer une source de données pour ArrayList

(2) Créez un flux. Puisqu'il est basé sur ArrayList \ ici La méthode Stream renvoie également un objet Stream \ .

③ Utilisez la méthode de filtrage pour "extraire uniquement les valeurs de plus de 5 caractères" "Convertir en majuscules" avec la méthode de la carte Il peut y avoir plusieurs processus intermédiaires. Vous pouvez l'omettre.

(4) Sortie de la valeur obtenue avec la méthode System.out :: println avec la méthode forEach. ** Le traitement de la résiliation ne peut être omis. ** **   La valeur de retour du traitement intermédiaire est Stream \ . Dans l'API Stream, l'opérateur "." Est utilisé pour effectuer le processus de la génération du flux au traitement intermédiaire / traitement de fin. Vous pouvez les connecter tous ensemble et écrire intelligemment. (Appelée ** chaîne de méthodes ** au sens de chaîne de méthodes)

Une série de traitements de flux est exécutée au moment du traitement de fin. Même si une opération telle que l'extraction / traitement est appelée au milieu, elle est une fois stockée et Il n'est pas exécuté sur place et attend le traitement jusqu'à la fin du traitement. C'est ce qu'on appelle le ** traitement du retard **.

Comment créer un flux

Généré à partir d'une collection / d'un tableau

** De la collection ** Collection.stream() ** À partir du tableau ** Arrays.stream(T[]) ** À partir de la carte ** Map.entrySet().stream()

Il existe également une méthode parallelStream () en tant que version parallèle de la méthode stream (). Le traitement parallèle est possible simplement en remplaçant stream par parallelStream. (fort…) Si le nombre d'éléments à traiter est important, il peut être possible de traiter efficacement en permettant un traitement parallèle. (Bien sûr, ce n'est pas toujours rapide à cause de la surcharge de la parallélisation.) Vous pouvez également paralléliser ou sérialiser des flux existants.

Génération de flux à partir de la classe Stream

Dans la classe Stream, il existe une méthode Factory pour créer un Stream. La méthode la plus élémentaire est la méthode of, qui convertit l'argument du numéro de variable spécifié en un Stream.

var stream = Stream.of("tokyo","osaka","nagoya");
stream.forEach(System.out::println); // tokyo, osaka, nagoya

Il existe également generate (), builder (), concat (), iterator (). Je vais l'omettre ici.

Type primitif Génération de flux

IntStream Stream spécialisé avec int LongStream Stream spécialisé avec long DoubleStream Stream spécialisé avec double

IntStream.range (int start, int endExclusive) [Second argument is out of range: open space] IntStream.rangeClosed (int start, int endInclusive) [Le deuxième argument est dans la plage: espace fermé]

Voici un exemple de traitement itératif utilisant IntStream. Comparez avec le cas de l'utilisation de l'instruction for. C'est un peu à la mode.

Répéter en utilisant l'instruction for


for(int i = 1; i <= 5 ; i++){
   System.out.println(i);
}

Répétez avec IntStream


IntStream.rangeClosed(1, 5).forEach(System.out::println);

Les types primitifs ne peuvent pas être utilisés dans les arguments de type génériques Java, donc L'écriture comme Stream \ entraînera une erreur.

Traitement intermédiaire

Il a pour rôle d'extraire / traiter les valeurs circulant dans le Stream. Le traitement intermédiaire est exécuté uniquement lorsque le traitement de fin est appelé. Il n'est pas exécuté à chaque fois qu'il est appelé. filter Extrait la valeur dans les conditions spécifiées.

Stream.of("tokyo", "nagoya", "osaka").filter(s -> s.startsWith("t")).forEach(System.out::println); //tokyo

map Traitez la valeur donnée.

Stream.of("tokyo", "nagoya", "osaka").map(s -> s.length)).forEach(System.out::println); //5, 6, 5

Notez qu'il s'agissait de Stream \ immédiatement après sa création, mais qu'il s'agit maintenant de Stream \ après la méthode de carte.

sorted Triez les éléments.

Stream.of("tokyo", "nagoya", "osaka").sorted().forEach(System.out::println); // nagoya, osaka, tokyo
Stream.of(2,1,3).sorted().forEach(System.out::println); // 1, 2, 3

Le comportement par défaut est le tri dans l'ordre naturel. S'il s'agit d'une chaîne de caractères, elle est triée dans l'ordre du dictionnaire, et s'il s'agit d'une valeur numérique, elle est triée par grande ou petite. Si vous souhaitez spécifier votre propre règle de tri, définissez la règle de tri avec une expression lambda. L'argument de sorted () est l'interface Composer.

Stream.of("tokyo", "nagoya", "osaka").
  sorted((str1, str2) -> str1.length() - str2.length()).forEach(System.out::println); // tokyo, osaka, nagoya

skip/limit sauter: sauter des éléments jusqu'à m limite: tronquer n + 1 et les éléments suivants

IntStream.range(1, 10).skip(3).limit(5).forEach(System.out::println); // 4, 5, 6, 7, 8

Passer les 4 premiers éléments avec la méthode skip La méthode limite en extrait 5 éléments. Faites attention à l'argument car la méthode limit opère sur un Stream qui a déjà été tronqué.

peek Vérifiez l'état intermédiaire de Stream. La méthode peek elle-même n'affecte pas Stream, elle est donc principalement utilisée pour le débogage.

Stream.of("tokyo", "nagoya", "osaka").peek(System.out::println).sorted().forEach(System.out::println);
//Résultat avant le tri: tokyo, nagoya,osaka ← coup d'oeil println
//Résultat après tri: nagoya, osaka,tokyo ← pour chaque println

distinct Supprimez les valeurs en double.

Stream.of("tokyo", "nagoya", "osaka", "osaka", "nagoya", "tokyo").distinct().forEach(System.out::println);
// tokyo, nagoya, osaka

Résiliation

Il a pour rôle de sortir / agréger enfin les valeurs circulant dans le Stream. Parce que Stream est finalement traité avec l'appel de traitement de terminaison comme déclencheur. Contrairement au traitement intermédiaire, le traitement de fin ne peut pas être omis.

Parce que le flux terminé ne peut pas être réutilisé Si vous souhaitez effectuer à nouveau le traitement du flux, vous devez régénérer le flux lui-même à partir de la source de données.

forEach Traitez les éléments individuels dans l'ordre.

Stream.of("tokyo", "nagoya", "osaka").forEach(v -> System.out.println(v)); // tokyo, nagoya, osaka
Stream.of("tokyo", "nagoya", "osaka").forEach(System.out::println); // tokyo, nagoya, osaka

findFirst Obtenez la première valeur.

System.out.println(Stream.of("tokyo", "nagoya", "osaka").filter(s -> s.startsWith("t")).findFirst().orElse("empty"));
// tokyo

La valeur de retour de la méthode findFirst est de type Facultatif car il peut s'agir d'un Stream vide.

Si vide


System.out.println(Stream.of("tokyo", "nagoya", "osaka").filter(s -> s.startsWith("a")).findFirst().orElse("empty"));
// empty

anyMatch/allMatch/noneMatch Détermine si la valeur répond à une condition particulière. Dans l'ordre, "s'il existe un élément pour lequel l'expression conditionnelle est vraie", "si toutes les expressions conditionnelles sont vraies", "Toutes les expressions conditionnelles ne sont-elles pas vraies?"

System.out.println(Stream.of("tokyo", "nagoya", "osaka").anyMatch(v -> v.length() == 5)); // true
System.out.println(Stream.of("tokyo", "nagoya", "osaka").allMatch(v -> v.length() == 5)); // false
System.out.println(Stream.of("tokyo", "nagoya", "osaka").noneMatch(v -> v.length() == 5)); // false

toArray Convertit le résultat du traitement Stream en un tableau de chaînes de caractères.

var list = Stream.of("tokyo", "nagoya", "osaka").filter(s -> s.startsWith("t")).toArray();

collect (conversion de collection)

Convertit le résultat du traitement Stream en une collection. Passez la méthode de conversion fournie par la classe Collectors à la méthode collect. Utilisez toList pour la conversion en List, toSet pour la conversion en Set et toMap pour la conversion en map.

var list = Stream.of("tokyo", "nagoya", "osaka").filter(s -> s.startsWith("t")).collect(Collectors.toList());

La méthode collect n'est pas une méthode dédiée à la conversion de collection C'est également une méthode qui effectue un traitement de réduction. Le cas du traitement de réduction sera décrit plus loin.

min/max Trouvez la valeur minimale / maximale. Il est nécessaire de spécifier une règle de comparaison (comparateur) comme argument. Étant donné que la valeur de retour est de type facultatif, ce sera via orElse. (C'est -1 ce qui signifie que ce n'est pas ici.)

System.out.println(Stream.of(1, 3, 2).min((int1, int2) -> int1 - int2).orElse(-1)); // 1
System.out.println(Stream.of(1, 3, 2).min((int1, int2) -> int2 - int1).orElse(-1)); // 3
System.out.println(Stream.of(8, 7, 9).max((int1, int2) -> int1 - int2).orElse(-1)); // 9
System.out.println(Stream.of(8, 7, 9).max((int1, int2) -> int2 - int1).orElse(-1)); // 7

count Trouvez le nombre d'éléments.

System.out.println(Stream.of("tokyo", "nagoya", "osaka").filter(s -> s.length() > 5).count()); // 1

reduce Combinez les valeurs de flux en une seule (réduction). La méthode de réduction fournit trois types de surcharge.

Avec un seul argument

Optional reduce(BinaryOperator accumulator); Étant donné que la valeur de retour est de type facultatif, ce sera via orElse.

Les arguments sont le résultat de la variable pour stocker le résultat de l'opération et la variable str pour recevoir des éléments individuels.

Avec un seul argument


System.out.println(
    Stream.of("tokyo", "nagoya", "osaka").sorted()
        .reduce((result, str) -> { return result + "," + str;}).orElse("")); // nagoya,osaka,tokyo

Avec 2 arguments

T reduce(T identity, BinaryOperator accumulator); Vous pouvez recevoir la valeur initiale avec le premier argument. Le résultat est évidemment non nul, il est donc de type non facultatif. Pas besoin de passer par OrElse.

Avec 2 arguments


System.out.println(
    Stream.of("tokyo", "nagoya", "osaka").sorted()
        .reduce("hokkaido",  (result, str) -> { return result + "," + str;})); //hokkaido,nagoya,osaka,tokyo

Avec 3 arguments

U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator combiner); Cela peut être un peu difficile. Il est utilisé lorsque le type d'élément de Stream et le type d'élément final sont différents. Un exemple est omis ici. Si vous souhaitez en savoir plus, vous pouvez consulter l'article ici.

collect (opération de réduction)

Collectez les éléments de Stream dans Collection, etc. Alors que la réduction réduit les éléments d'un Stream à une valeur unique telle que int ou String, collect accumule et renvoie des valeurs pour des conteneurs de variables tels que Collection / StringBuilder (réduction de variables).

C'est un peu ésotérique, je le mettrai donc à jour plus tard ...

À la fin

Je prévois d'obtenir Java Gold SE 11 d'ici mars. (En premier lieu, vous devez prendre Java Silver SE 11 comme prérequis ...) Dans Gold, le taux de question de l'expression lambda / API Stream est très élevé, je l'ai donc organisé.

Java SE 11 est une qualification qui vient de s'achever il y a environ six mois (fin 2019/06). Jusqu'à présent, SE 8 était la dernière version. Un ouvrage de référence pour Silver SE 11 a déjà été publié, Gold SE 11 n'a pas été publié comme ouvrage de référence pour le moment (décembre 2019). Contrairement au test SE 8, le test SE 11 utilise l'expression lambda / API Stream. Je pense que ce sera basé sur SE 11, alors soyez prudent si vous passez l'examen.

Recommended Posts