Considérez un List<String> stringList
qui peut être imprimé de plusieurs façons en utilisant des constructions Java 8 :
stringList.forEach(System.out::println); // 1) Iterable.forEach
stringList.stream().forEach(System.out::println); // 2) Stream.forEach (order maintained generally but doc does not guarantee)
stringList.stream().forEachOrdered(System.out::println); // 3) Stream.forEachOrdered (order maintained always)
stringList.parallelStream().forEach(System.out::println); // 4) Parallel version of Stream.forEach (order not maintained)
stringList.parallelStream().forEachOrdered(System.out::println); // 5) Parallel version ofStream.forEachOrdered (order maintained always)
En quoi ces approches sont-elles différentes les unes des autres?
Première approche ( Iterable.forEach
) -
L'itérateur de la collection est généralement utilisé et il est conçu pour être infaillible, ce qui signifie qu'il sera lancé ConcurrentModificationException
si la collection sous-jacente est structurellement modifiée pendant l'itération. Comme mentionné dans le doc pour ArrayList
:
Une modification structurelle est toute opération qui ajoute ou supprime un ou plusieurs éléments, ou redimensionne explicitement le tableau de support; la simple définition de la valeur d'un élément n'est pas une modification structurelle.
Cela signifie donc que la ArrayList.forEach
définition de la valeur est autorisée sans aucun problème. Et en cas de collecte simultanée, par exemple, ConcurrentLinkedQueue
l'itérateur serait faiblement cohérent, ce qui signifie que les actions transmises forEach
sont autorisées à effectuer des changements structurels réguliers sans ConcurrentModificationException
exception. Mais ici, les modifications peuvent être visibles ou non dans cette itération.
Deuxième approche ( Stream.forEach
) -
L'ordre n'est pas défini. Bien que cela ne puisse pas se produire pour les flux séquentiels, la spécification ne le garantit pas. L'action doit également être de nature non interférente. Comme mentionné dans le doc :
Le comportement de cette opération est explicitement non déterministe. Pour les pipelines de flux parallèles, cette opération ne garantit pas de respecter l'ordre de rencontre du flux, car cela sacrifierait l'avantage du parallélisme.
Troisième approche ( Stream.forEachOrdered
) -
L'action serait exécutée dans l'ordre de rencontre du flux. Donc, chaque fois que l'ordre compte, utilisez forEachOrdered
sans arrière-pensée. Comme mentionné dans le doc :
Effectue une action pour chaque élément de ce flux, dans l' ordre de rencontre du flux si le flux a un ordre de rencontre défini.
Lors de l'itération sur une collection synchronisée, la première approche prendrait le verrou de la collection une fois et le maintiendrait sur tous les appels à la méthode d'action, mais dans le cas des flux, ils utilisent le séparateur de la collection, qui ne se verrouille pas et s'appuie sur les règles de non -ingérence. Dans le cas où la collection sauvegardant le flux est modifiée pendant l'itération, un résultat ConcurrentModificationException
serait levé ou un résultat incohérent pourrait se produire.
Quatrième approche (parallèle Stream.forEach
) -
Comme déjà mentionné, aucune garantie de respecter l'ordre de rencontre comme prévu en cas de flux parallèles. Il est possible que l'action soit effectuée dans différents threads pour différents éléments, ce qui ne peut jamais être le cas forEachOrdered
.
Cinquième approche (parallèle Stream.forEachOrdered
) -
Le forEachOrdered
traitera les éléments dans l'ordre spécifié par la source, que le flux soit séquentiel ou parallèle. Cela n'a donc aucun sens de l'utiliser avec des flux parallèles.
List
être? Montrez-nous comment vous l'avez déclaré et instancié.