Comment garantir l'ordre de traitement dans les flux java8?

148

Je veux traiter des listes dans un XMLobjet java. Je dois m'assurer du traitement de tous les éléments afin de les recevoir.

Dois-je donc faire appel sequentialà chacun streamque j'utilise? list.stream().sequential().filter().forEach()

Ou est-ce suffisant d'utiliser simplement le flux tant que je n'utilise pas de parallélisme? list.stream().filter().forEach()

membres
la source

Réponses:

339

Vous posez la mauvaise question. Vous demandez sequentialplutôt parallelque vous voulez traiter les articles dans l'ordre , vous devez donc vous renseigner sur la commande . Si vous avez un flux ordonné et effectuez des opérations qui garantissent le maintien de l'ordre, peu importe que le flux soit traité en parallèle ou séquentiel; la mise en œuvre maintiendra l'ordre.

La propriété ordonnée est distincte de parallèle vs séquentielle. Par exemple, si vous appelez stream()sur un, HashSetle flux ne sera pas ordonné lors de l'appel stream()sur un Listrenvoie un flux ordonné. Notez que vous pouvez appeler unordered()pour libérer le contrat de commande et augmenter potentiellement les performances. Une fois que le flux n'a pas de commande, il n'y a aucun moyen de rétablir la commande. (La seule façon de transformer un flux non ordonné en un flux ordonné est d'appeler sorted, cependant, l'ordre résultant n'est pas nécessairement l'ordre d'origine).

Voir également la section «Commande» de la java.util.streamdocumentation du package .

Afin d'assurer le maintien de la commande tout au long d'une opération de flux, vous devez étudier la documentation de la source du flux, toutes les opérations intermédiaires et le fonctionnement du terminal pour savoir si elles maintiennent l'ordre ou non (ou si la source a une commande dans le premier endroit).

Cela peut être très subtil, par exemple, Stream.iterate(T,UnaryOperator)crée un flux ordonné tout en Stream.generate(Supplier)créant un flux non ordonné . Notez que vous avez également commis une erreur courante dans votre question car elle ne maintient pas la commande. Vous devez utiliser si vous souhaitez traiter les éléments du flux dans un ordre garanti.forEach forEachOrdered

Donc, si votre listdans votre question est effectivement a java.util.List, sa stream()méthode retournera un flux ordonné et filterne changera pas l'ordre. Donc si vous appelez list.stream().filter() .forEachOrdered(), tous les éléments seront traités séquentiellement dans l'ordre, alors que pour list.parallelStream().filter().forEachOrdered()les éléments pourraient être traités en parallèle (par exemple par le filtre) mais l'action du terminal sera toujours appelée dans l'ordre (ce qui réduira évidemment le bénéfice de l'exécution en parallèle) .

Si, par exemple, vous utilisez une opération comme

List<…> result=inputList.parallelStream().map(…).filter(…).collect(Collectors.toList());

toute l'opération peut bénéficier d'une exécution parallèle, mais la liste résultante sera toujours dans le bon ordre, que vous utilisiez un flux parallèle ou séquentiel.

Holger
la source
48
Oui, bonne réponse. Une chose que j'ai trouvée est que la terminologie que nous utilisons, au moins en anglais, comme «avant», «après», etc., est assez ambiguë. Il existe deux types d'ordre ici: 1) l' ordre de rencontre (également appelé ordre spatial ) et 2) l' ordre de traitement (également appelé ordre temporel ). En gardant cette distinction à l'esprit, il peut être utile d'utiliser des mots tels que «à gauche de» ou «à droite de» pour discuter de l'ordre de rencontre et «avant» ou «plus tard que» lors de l'examen de l'ordre de traitement.
Stuart marque
Je comprends List<>que la commande sera conservée, mais le fera- Collection<>t- elle ?
Josh C.
5
@JoshC. cela dépend du type de collection réel. Sets généralement pas, sauf si c'est un SortedSetou LinkedHashSet. Les vues de collecte d'un Map( keySet(), entrySet()et values()) héritent de la Mappolitique de, ce sont commandés lorsque la carte est un SortedMapou LinkedHashMap. Le comportement est déterminé par les caractéristiques rapportées par le séparateur de la collection . L' defaultimplémentation de Collectionne signale pas la ORDEREDcaractéristique, elle n'est donc pas ordonnée, sauf si elle est remplacée.
Holger
@Holger J'avais une question qui pourrait être liée quelque peu à une petite partie de votre réponse.
Naman le
1
Il convient de noter que cela forEachOrderedne diffère que forEachlorsque vous utilisez des flux parallèles - mais il est recommandé de l'utiliser quand même lors de la commande, au cas où la méthode de cuisson à la vapeur changerait un jour ...
Steve Chambers
0

En un mot:

L'ordre dépend de la structure des données source et des opérations de flux intermédiaires. En supposant que vous utilisez un, Listle traitement doit être ordonné (car filtercela ne changera pas la séquence ici).

Plus de détails:

Séquentiel vs parallèle vs non ordonné:

Javadocs

S sequential()
Returns an equivalent stream that is sequential. May return itself, either because the stream was already sequential, or because the underlying stream state was modified to be sequential.
This is an intermediate operation.
S parallel()
Returns an equivalent stream that is parallel. May return itself, either because the stream was already parallel, or because the underlying stream state was modified to be parallel.
This is an intermediate operation.
S unordered()
Returns an equivalent stream that is unordered. May return itself, either because the stream was already unordered, or because the underlying stream state was modified to be unordered.
This is an intermediate operation.

Commande de flux:

Javadocs

Les flux peuvent avoir ou non un ordre de rencontre défini. Le fait qu'un flux ait ou non un ordre de rencontre dépend de la source et des opérations intermédiaires. Certaines sources de flux (telles que List ou tableaux) sont intrinsèquement ordonnées, tandis que d'autres (telles que HashSet) ne le sont pas. Certaines opérations intermédiaires, telles que sorted (), peuvent imposer un ordre de rencontre sur un flux autrement non ordonné, et d'autres peuvent rendre un flux ordonné non ordonné, comme BaseStream.unordered (). De plus, certaines opérations de terminal peuvent ignorer l'ordre de rencontre, comme forEach ().

Si un flux est ordonné, la plupart des opérations sont contraintes d'opérer sur les éléments dans leur ordre de rencontre; si la source d'un flux est une liste contenant [1, 2, 3], alors le résultat de l'exécution de la carte (x -> x * 2) doit être [2, 4, 6]. Cependant, si la source n'a pas d'ordre de rencontre défini, alors toute permutation des valeurs [2, 4, 6] serait un résultat valide.

Pour les flux séquentiels, la présence ou l'absence d'un ordre de rencontre n'affecte pas les performances, uniquement le déterminisme. Si un flux est commandé, l'exécution répétée de pipelines de flux identiques sur une source identique produira un résultat identique; s'il n'est pas ordonné, une exécution répétée peut produire des résultats différents.

Pour les flux parallèles, l'assouplissement de la contrainte de classement peut parfois permettre une exécution plus efficace. Certaines opérations d'agrégation, telles que le filtrage des doublons (distinct ()) ou des réductions groupées (Collectors.groupingBy ()) peuvent être implémentées plus efficacement si l'ordre des éléments n'est pas pertinent. De même, les opérations qui sont intrinsèquement liées à l'ordre des rencontres, telles que limit (), peuvent nécessiter une mise en mémoire tampon pour garantir un ordre correct, ce qui compromet l'avantage du parallélisme. Dans les cas où le flux a un ordre de rencontre, mais que l'utilisateur ne se soucie pas particulièrement de cet ordre de rencontre, désordonner explicitement le flux avec unordered () peut améliorer les performances parallèles pour certaines opérations avec état ou terminal. Cependant, la plupart des pipelines de flux, tels que l'exemple "somme des poids des blocs" ci-dessus,

Saikat
la source