2013-10-22 17 views
25

JDK 8 EA è ora disponibile e sto solo cercando di abituarmi al lambda e alla nuova API Stream. Ho cercato di ordinare un elenco con flusso parallelo, ma il risultato è sempre sbagliato:java 8 parallelStream() con ordinato()

import java.util.ArrayList; 
import java.util.List; 

public class Test 
{ 
    public static void main(String[] args) 
    { 
     List<String> list = new ArrayList<>(); 
     list.add("C"); 
     list.add("H"); 
     list.add("A"); 
     list.add("A"); 
     list.add("B"); 
     list.add("F"); 
     list.add(""); 

     list.parallelStream() // in parallel, not just concurrently! 
      .filter(s -> !s.isEmpty()) // remove empty strings 
      .distinct() // remove duplicates 
      .sorted() // sort them 
      .forEach(s -> System.out.println(s)); // print each item 
    } 
} 

USCITA:

C 
F 
B 
H 
A 

Nota che ogni volta che l'uscita è diverso. Le mie domande sono, è un bug? o non è possibile ordinare una lista in parallelo? se è così, allora perché il JavaDoc non lo dice? Ultima domanda, c'è un'altra operazione il cui output sarebbe diverso a seconda del tipo di stream?

+1

Probabilmente sarebbe meglio rimuovere i duplicati dopo l'ordinamento. – Ingo

risposta

44

È necessario utilizzare forEachOrdered, non forEach.

Come per il forEach doc:

Per oleodotti flusso parallelo, questa operazione non garantisce di rispettare l'ordine incontro del torrente, in quanto così facendo avrebbe sacrificato il beneficio di parallelismo. Per ogni dato elemento, l'azione può essere eseguita in qualsiasi momento e in qualsiasi thread che la libreria sceglie. Se l'azione accede allo stato condiviso, è responsabile della fornitura della sincronizzazione richiesta.

+1

+1 Splendido! Questo è quello che stavo cercando. –

+0

La mia ipotesi è che internamente si stia creando un elenco "ordinato", ogni thread viene aggiunto a quell'elenco, quindi continua con il passaggio successivo nel flusso (forEach) quindi viene eseguito fuori servizio, FWIW. – rogerdpack

6

Inoltre, è possibile leggere ulteriori informazioni sul parallelismo e forEach Order con un esempio molto bello da here. In sintesi, l'utilizzo di forEachOrdered in un flusso parallelo potrebbe comportare la perdita dei vantaggi del parallelismo.

Ecco l'esempio della stessa risorsa:

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

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

System.out.println("listOfIntegers sorted in reverse order:"); 
Comparator<Integer> normal = Integer::compare; 
Comparator<Integer> reversed = normal.reversed(); 
Collections.sort(listOfIntegers, reversed); 
listOfIntegers 
    .stream() 
    .forEach(e -> System.out.print(e + " ")); 
System.out.println(""); 

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

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

System.out.println("With forEachOrdered:"); 
listOfIntegers 
    .parallelStream() 
    .forEachOrdered(e -> System.out.print(e + " ")); 
System.out.println(""); 

e l'uscita è

listOfIntegers: 
1 2 3 4 5 6 7 8 
listOfIntegers sorted in reverse order: 
8 7 6 5 4 3 2 1 
Parallel stream: 
3 4 1 6 2 5 7 8 
Another parallel stream: 
6 3 1 5 7 8 4 2 
With forEachOrdered: 
8 7 6 5 4 3 2 1 

La quinta gasdotto utilizza il metodo forEachOrdered, che elabora i elementi del flusso nella ordine specificato dalla sua origine, indipendentemente dal fatto che sia stato eseguito lo streaming in serie o parallelo. noti che si rischia di perdere i vantaggi di parallelismo se si utilizza operazioni come forEachOrdered con flussi paralleli

.

+0

Questo è un po 'sottile. Si prega di espandere la risposta modificando. –

Problemi correlati