2015-04-17 2 views
26

Perché forEach stampa i numeri in ordine casuale, mentre collect raccoglie sempre gli elementi nell'ordine originale, anche dallo streaming parallelo?Perché lo stream parallelo viene raccolto sequenzialmente in Java 8

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); 

uscita:

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

risposta

32

Qui ci sono due diversi tipi di "ordinamento" che rendono confusa la discussione.

Un tipo è ordine di incontro, che è definito nello streams documentation. Un buon modo per pensare a questo è lo o da sinistra a destra nello spazio nell'ordine di elementi nella raccolta di origine. Se la fonte è un List, considera gli elementi precedenti che si trovano a sinistra degli elementi successivi.

C'è anche elaborazione o temporale ordine, che non è definito nella documentazione, ma che è l'ordine temporale in cui elementi sono elaborati da diversi thread. Se gli elementi di un elenco vengono elaborati in parallelo da thread diversi, un thread potrebbe elaborare l'elemento più a destra nell'elenco prima dell'elemento più a sinistra. Ma la prossima volta potrebbe non farlo.

Anche quando i calcoli verranno eseguiti in parallelo, più Collectors e alcune operazioni del terminale vengono accuratamente disposti in modo che esse mantengono ordine incontro dalla sorgente fino alla destinazione, indipendentemente dal ordine temporale in cui diversi thread potrebbero elaborare ogni elemento.

Si noti che l'operazione del terminale forEach fa non conserva ordine incontro. Invece, è gestito da qualsiasi thread che si verifica per produrre il risultato successivo. Se vuoi qualcosa come forEach che conserva l'ordine dell'incontro, usa invece forEachOrdered.

Vedere anche lo Lambda FAQ per ulteriori discussioni sui problemi di ordinazione.

+0

grazie! Ottima spiegazione –

9

Il Collectors.toList method specifica che la tornata Collector aggiunge elementi alla lista in ordine all'incontro.

Returns:

un collettore che raccoglie tutti gli elementi di ingresso in una lista, in ordine all'incontro

Non importa se il Stream è parallela; l'ordine è conservato.

Inoltre, guardando il codice sorgente Collectors, il restituita Collector chiamate addAll su un ArrayList quando si uniscono, e che conserva l'ordine. Per esempio. se un thread ha {1, 2} e il thread successivo ha {3, 4}, la chiamata a addAll produce {1, 2, 3, 4}. Inoltre, il reso Collector non ha la caratteristica UNORDERED.

+0

Come può essere noto l'ordine dopo che è stato richiamato 'parallelo'? –

+2

Cosa ti fa pensare che 'parallel' scarterebbe l'ordine? –

+0

Scarterebbe l'ordine una volta raggiunta l'operazione del terminale. Non necessariamente, dipende dalla CPU e dal sistema operativo, ma il primo esempio 'forEach' lo dimostra. –