web-dev-qa-db-fra.com

Pourquoi le flux parallèle est collecté séquentiellement dans Java 8

Pourquoi forEach imprime les nombres dans un ordre aléatoire, tandis que collect recueille toujours les éléments dans l'ordre d'origine, même à partir d'un flux parallèle?

Integer[] intArray = {1, 2, 3, 4, 5, 6, 7, 8};
List<Integer> listOfIntegers = new ArrayList<>(Arrays.asList(intArray));

System.out.println("Parallel Stream: ");
listOfIntegers
  .stream()
  .parallel()
  .forEach(e -> System.out.print(e + " "));
System.out.println();

// Collectors         
List<Integer> l = listOfIntegers
  .stream()
  .parallel()
  .collect(Collectors.toList());
System.out.println(l);

Production:

Parallel Stream: 
8 1 6 2 7 4 5 3 
[1, 2, 3, 4, 5, 6, 7, 8]
34
Barat Sahdzijeu

Il existe deux types différents de "classement" en cours, ce qui rend la discussion confuse.

Un type est ordre de rencontre, qui est défini dans la documentation des flux . Une bonne façon de penser à cela est l'ordre spatial ou de gauche à droite des éléments de la collection source. Si la source est un List, considérez que les éléments antérieurs se trouvent à gauche des éléments ultérieurs.

Il existe également un ordre processing ou temporel, qui n'est pas défini dans la documentation, mais qui est l'ordre temporel dans lequel les éléments sont traités par différents threads. Si les éléments d'une liste sont traités en parallèle par différents threads, un thread peut traiter l'élément le plus à droite de la liste avant l'élément le plus à gauche. Mais la prochaine fois, ce ne sera peut-être pas le cas.

Même lorsque les calculs sont effectués en parallèle, la plupart des Collectors et certaines opérations de terminal sont soigneusement organisées de manière à conserver ordre de rencontre de la source à la destination, indépendamment du temporal order dans lequel différents threads pourraient traiter chaque élément.

Notez que l'opération de terminal forEach ne pas préserve l'ordre de rencontre. Au lieu de cela, il est exécuté par le thread qui produit le résultat suivant. Si vous voulez quelque chose comme forEach qui préserve l'ordre des rencontres, utilisez plutôt forEachOrdered.

Voir également Lambda FAQ pour plus de détails sur les problèmes de commande.

44
Stuart Marks

Le Collectors.toList method spécifie que le Collector retourné ajoute des éléments à la liste dans l'ordre de rencontre.

Retour:

un collecteur qui rassemble tous les éléments d'entrée dans une liste, dans l'ordre de rencontre

Peu importe que Stream soit parallèle; l'ordre est conservé.

De plus, en regardant le code source Collectors, le Collector renvoyé appelle addAll sur un ArrayList lors de la fusion, ce qui préserve l'ordre. Par exemple. si un thread a {1, 2} et le thread suivant a {3, 4}, alors l'appel à addAll donne {1, 2, 3, 4}. De plus, le Collector renvoyé n'a pas la caractéristique UNORDERED.

10
rgettman