Considera un List<String> stringListche può essere stampato in molti modi usando costrutti 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)
In che modo questi approcci sono diversi l'uno dall'altro?
Primo approccio ( Iterable.forEach) -
L'iteratore della raccolta viene generalmente utilizzato e progettato per essere fail-fast, il che significa che verrà lanciato ConcurrentModificationExceptionse la raccolta sottostante viene modificata strutturalmente durante l'iterazione. Come menzionato nel documento per ArrayList:
Una modifica strutturale è qualsiasi operazione che aggiunge o elimina uno o più elementi o ridimensiona esplicitamente l'array di supporto; la semplice impostazione del valore di un elemento non è una modifica strutturale.
Quindi significa ArrayList.forEachche l'impostazione del valore è consentita senza alcun problema. E in caso di raccolta simultanea, ad es. ConcurrentLinkedQueueL'iteratore sarebbe debolmente coerente, il che significa che alle azioni passate forEachè consentito apportare anche cambiamenti strutturali senza ConcurrentModificationExceptioneccezioni. Ma qui le modifiche potrebbero o meno essere visibili in quella iterazione.
Secondo approccio ( Stream.forEach):
l'ordine non è definito. Anche se potrebbe non verificarsi per flussi sequenziali, ma le specifiche non lo garantiscono. Inoltre, l'azione deve essere di natura non interferente. Come menzionato nel documento :
Il comportamento di questa operazione è esplicitamente non deterministico. Per le condotte di flusso parallelo, questa operazione non garantisce il rispetto dell'ordine di incontro del flusso, poiché ciò sacrificherebbe il vantaggio del parallelismo.
Terzo Approccio (Stream.forEachOrdered ):
l'azione verrà eseguita nell'ordine di incontro del flusso. Quindi, ogni volta che l'ordine conta, usa forEachOrderedsenza pensarci due volte. Come menzionato nel documento :
Esegue un'azione per ciascun elemento di questo flusso, nell'ordine di incontro del flusso se il flusso ha un ordine di incontro definito.
Durante l'iterazione su a raccolta sincronizzata, il primo approccio prenderebbe una volta il blocco della raccolta e lo terrà su tutti i metodi di invito all'azione, ma in caso di flussi usano lo splitterator della raccolta, che non si blocca e si basa sulle regole già stabilite di non -interference. Nel caso in cui il backup della raccolta venga modificato durante lo iterazione, lo stream ConcurrentModificationExceptionverrebbe generato o potrebbe verificarsi un risultato incoerente.
Quarto approccio (parallelo Stream.forEach ) -
Come già accennato, nessuna garanzia di rispettare l'ordine dell'incontro come previsto in caso di flussi paralleli. È possibile che l'azione venga eseguita in thread diversi per elementi diversi, cosa che non è mai possibile forEachOrdered.
Quinto Approach (parallelo Stream.forEachOrdered) -
La forEachOrderedelaborerà gli elementi nell'ordine specificato dalla sorgente indipendentemente dal fatto se stream è sequenziale o parallelo. Quindi non ha senso usarlo con flussi paralleli.
Listdi essere? Mostraci come l'hai dichiarato e istanziato.