À propos de l'opération de réduction du flux Java8

Dans Java8 Stream Rough Summary, j'ai présenté la plupart des opérations Stream, mais j'écrirai sur les opérations de réduction car il semble qu'une explication détaillée soit nécessaire. Fondamentalement, je me réfère à JavaDoc of Stream.

Qu'est-ce que l'opération de réduction?

Une opération de réduction est une opération qui renvoie le résultat de la combinaison de tous les éléments d'un flux en un seul à l'aide d'une fonction cumulative. Il semble que la réduction s'appelle convolution en japonais, mais ce n'est pas une soi-disant convolution. Cela ressemble plus à une puissance totale ou totale (ou plutôt, une puissance totale ou totale est un type de réduction). Les opérations de réduction incluent des réductions qui renvoient une valeur unique et des réductions variables qui renvoient un conteneur contenant plusieurs valeurs (comme une collection).

réduire ~ réduction ~

La méthode reduction renvoie le résultat de l'accumulation de chaque élément en utilisant une fonction cumulative (ʻaccumulator). Il existe trois types de remplacements dans la méthode reduction`:

Méthode
T reduce(T identity, BinaryOperator<T> accumulator)
Optional<T> reduce(BinaryOperator<T> accumulator)
<U> U reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)

T: Type d'élément dans Stream

Chaque argument

accumulateur ~ Fonction cumulative ~

ʻAccumulatorest une fonction (interface) pour calculer le résultat cumulatif en répétant la somme des éléments. Dans la méthodereduction, ʻaccumulator.apply est appelé en interne pour additionner les deux valeurs afin de générer un résultat intermédiaire, et le processus de totalisation est répété pour le résultat intermédiaire afin d'obtenir le résultat final. .. Par conséquent, ʻaccumulator.apply` doit être une opération pour laquelle la loi de combinaison est valable.

identité ~ élément unitaire ~

ʻIndetify est l'élément unitaire de ʻaccumulator.apply.

Définition mathématique de l'élément unitaire

Élément arbitraire a dans l'ensemble et opérations binomiales dessus*E qui satisfait les propriétés suivantes est appelé un élément unitaire.
a * e = e * a = a

Expression de type Java

Un identifiant qui satisfait les propriétés suivantes pour tout élément a du flux est appelé un élément unité.
accumulator.apply(identify, a) == accumulator.apply(a, identify) == a

Pour donner un exemple concret, l'élément unité de + (additionnel) sur l'ensemble de nombres réels est 0, et l'élément unité de * (multiplication) est 1.

Quand a = 2

0 + 2 = 2 + 0 = 2
1 * 2 = 2 * 1 = 2

Si aucun élément du flux n'existe, la valeur d'origine est renvoyée suite à la réduction. Si vous ne spécifiez pas d'élément unit (la deuxième méthode de substitution), vous devez le spécifier autant que possible car cela simplifie le processus à l'intérieur de `reduction '(en particulier pour le traitement parallèle).

combinateur ~ fonction combiner ~

combiner est une fonction (interface) pour combiner des résultats cumulatifs. Dans les flux parallèles, combinez les résultats exécutés dans chaque thread. Si vous ne spécifiez pas combiner (première et deuxième méthodes de remplacement), ce processus de jointure est effectué par ʻaccumulator. De plus, pour les flux séquentiels, même si vous spécifiez combiner`, il ne sera pas utilisé.

combiner est le même que ʻaccumulator, l'élément unité doit être ʻidentity, et ce doit être une fonction qui satisfait la loi de combinaison, et il doit satisfaire ce qui suit (compatible avec ʻaccumulator`).

combiner.apply(u, accumulator.apply(identity, t)) == accumulator.apply(u, t)

Le cas où «combineur» est requis est lorsque les fonctions à exécuter lors de la sommation des éléments et lors de la combinaison des résultats cumulatifs sont différentes. Par exemple, la somme des carrés est le cas. Dans le cas de la somme des carrés, c'est $ a ^ 2 + b ^ 2 $ entre les éléments, mais si vous utilisez la même fonction lors de la combinaison des résultats, $ (a ^ 2 + b ^ 2) ^ 2 + ( Ce sera c ^ 2 + d ^ 2) ^ 2 $. Par conséquent, la réduction de la somme des carrés peut être réalisée en faisant du "combineur" une simple somme.

Stream.iterate(1, x->x+1)
      .limit(4)
      .parallel()
      .reduce(0, (x,y)->x*x + y*y, (x,y)->x+y);

Ce processus prend la somme des carrés de 1 à 4, donc le résultat est 30. Cependant, si vous n'utilisez pas «combiner», le résultat sera différent. Comme il est difficile de vérifier l'ordre de calcul dans un traitement parallèle, si vous passez au traitement séquentiel et exécutez ce traitement, le résultat sera 1172. C'est le résultat des calculs suivants:

(((((0^2+1^2)^2)+2^2)^2+3^2)^2+4^2) = 1172

Cependant, cette forme de traitement peut être simplement exprimée en utilisant «map». Pour la somme des carrés, tout ce que vous avez à faire est de trouver le carré de chaque élément avec map puis de le réduire.

Stream.iterate(1, x->x+1)
      .limit(4)
      .parallel()
      .map(x->x*x)
      .reduce(0, (x,y)->x*x + y*y);

collecter ~ réduction variable ~

La méthode collect renvoie un conteneur de résultats mutable au lieu d'une valeur comme résultat. Il existe deux types de remplacements pour la méthode collect.

Méthode
<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
<R,A> R collect(Collector<? super T,A,R> collector)

Méthode par opération de conteneur

La première méthode consiste à définir vous-même chaque opération par le conteneur de résultats. Si vous passez respectivement les opérations de création, d'ajout et de jointure, chaque opération sera utilisée pour la réduction.

fournisseur ~ générer ~

fournisseur est le processus de création d'une instance du conteneur de résultats. En gros, vous passerez souvent la result container class :: new, mais si vous avez une classe Factory, vous pouvez aussi passer sa méthode de création. Pour le traitement parallèle, le fournisseur est appelé plusieurs fois, mais à chaque fois, une nouvelle instance doit être créée.

accumulateur ~ ajouté ~

ʻAccumulator est le processus pour ajouter un élément au conteneur de résultats. Pour les objets de type Collection, la méthode ʻadd est applicable. Ce doit être un processus qui détient la loi de la connexion.

combineur ~ combiner ~

«combiner» est le processus de combinaison de deux conteneurs de résultats. Pour le traitement parallèle, les méthodes de résultat créées dans chaque thread sont combinées par «combineur». Pour les objets de type Collection, la méthode ʻaddAll` est applicable. Comme pour le traitement de réduction normal, la loi de couplage doit tenir et le traitement doit être compatible avec "accumulateur". De plus, il n'est pas utilisé pour les flux séquentiels.

Par exemple, le processus de réduction qui stocke les éléments dans le flux dans ArrayList et les renvoie est le suivant.

Stream.iterate(1, x->x+1)
      .limit(10)
      .parallel()
      .collect(ArrayList::new,
               Collection::add,
               Collection::addAll));

Comment utiliser Collcetor

Collector est un objet qui encapsule les opérations «fournisseur», «accumulateur» et «combineur». Le collecteur lui-même est une interface, et en plus des trois opérations ci-dessus, la méthode «caractéristiques» qui renvoie l'ensemble des caractéristiques de la collection et la méthode «finisseur» qui exécute le processus de conversion final sont définies. Vous pouvez créer votre propre objet Collector, mais la classe Collctors possède de nombreuses méthodes statiques qui renvoient des Collecteurs utiles.

Par exemple, le processus de création d'une liste comme précédemment peut être décrit comme suit en utilisant la méthode toList.

Stream.iterate(1, x->x+1)
      .limit(10)
      .parallel()
      .collect(Collectors.toList()));

Cependant, toList ne garantit pas le type de liste retourné (bien que ce soit ArrayList lorsque je l'ai essayé dans le processus ci-dessus). Si vous voulez décider du type de Collection à utiliser plus en détail, il existe une méthode toCollection.

Stream.iterate(1, x->x+1)
      .limit(10)
      .parallel()
      .collect(Collectors.toCollection(ArrayList::new)));

Dans la méthode toCollection, seul le fournisseur (processus de génération) est passé. N'importe quelle classe d'implémentation de Collection peut être utilisée, donc il peut s'agir de HashSet (bien qu'il existe également une méthode appelée toSet pour Set).

Il existe de nombreuses méthodes pratiques comme celle-ci dans la classe Collectors, vous pouvez donc facilement utiliser la réduction de variable en les utilisant.

Recommended Posts

À propos de l'opération de réduction du flux Java8
[Java] Opération intermédiaire de l'API Stream
Étudier Java 8 (Stream)
Terminaison du flux Java
[Java] Traitement de flux
Java 9 Facultatif :: stream
[Java] Fonctionnement du système de fichiers
[Java] Remarque sur les collecteurs de flux
[Java] Génération de flux API-Stream
[Java] API / carte de flux
Pratique de l'API Java8 Stream
Résumé approximatif du flux Java8
[Java11] Résumé du flux -Avantages du flux-
Principes de base de l'utilisation des caractères (Java)
Aide-mémoire de l'API Java Stream
API Java Stream en 5 minutes
Flux Java8, résumé de l'expression lambda
[Java] Stream API - Traitement de l'arrêt du flux
[Java] Stream API - Traitement intermédiaire de flux
Java Stream ne peut pas être réutilisé.
[Java] Introduction à l'API Stream
[Java11] Résumé de l'utilisation du flux -Basics-
Application Java pour les débutants: stream
[Java 8] Suppression en double (et vérification en double) avec Stream
[Java] Stream (filtrer, mapper, forEach, réduire)
[java8] Pour comprendre l'API Stream
À propos de Lambda, Stream, LocalDate de Java8
[Introduction à Java] À propos de l'API Stream
[Java] Vérification de l'existence des éléments avec Stream
Java
J'ai essayé d'utiliser l'API Java8 Stream
Flux de traitement de base de Java Stream
Java
Java 8 ~ Stream API ~ pour commencer maintenant
Liste de conversion mutuelle de tableau / liste / flux Java
Conversion de liste Java8 avec Stream map
Utilisez-vous Stream en Java?
Essayez d'utiliser l'API Stream en Java
De nos jours, les expressions Java Lambda et l'API de flux
Essayez différentes méthodes d'API Java Stream (maintenant)
[Java] Comparaison des méthodes d'opération de la collection et de StringBuilder
Méthode d'opération d'élément dans Appium TIPS (Java)