2014-05-02 12 views
9

Ho una lista di elementi, ho bisogno di trovare il primo elemento che soddisfa la condizione quindi uscire utilizzando Java 8 flussi.Stream con Sort() prima di findFirst() non è più pigro

penso che il seguente codice purtroppo valutare tutti gli elementi a disposizione, che non quello che ho bisogno, ho bisogno di valutare gli elementi uno ad uno e stop (break) quando trovare la prima partita:

sto qui Ordinamento della elementi, quindi mappare l'elemento al suo attributo url quindi provare a filtrare se lo url non è nullo o vuoto, quindi trovare la corrispondenza first!

Arrays.stream(dataArray) 
.sorted(Comparator.comparing(d -> d.getPriority())) 
.peek(o -> System.out.println("SORT: " + o)) 
.map(d -> d.getOriginalURL(shortUrl)) 
.peek(o -> System.out.println("MAP: " + o)) 
.filter(u -> u != null && !u.isEmpty()) 
.peek(o -> System.out.println("FILTER: " + o)) 
.findFirst().orElse(""); 

Ma l'output mostra che, tutti gli elementi sono evaulated, anche se il primo corrisponde l'operazione if condizione (filter).

Data[] data = new Data[] { new ParseData(), new InMemoryData() }; 
System.out.println(">>> " + getOriginalURL(data, "")); 

OUTPUT:

SORT: [email protected] 
MAP: InMemory URL 
FILTER: InMemory URL 
SORT: [email protected] 
MAP: Parse.com URL   <<< THIS SHOULD NOT HAPPEN 
FILTER: Parse.com URL  <<< AND THIS TOO 
>>> InMemory URL 

Come dimostra uscita, il flusso non si ferma quando il filtro corrisponde al primo elemento, invece procedi valutare il secondo elemento troppo!

che voglio fare in questo modo:

Arrays.sort(dataArray, Comparator.comparing(d -> d.getPriority())); // sort 

for (Data data : dataArray) { 
    String url = data.getOriginalURL(shortUrl);   // map 
    if (url != null && !url.isEmpty()) {     // filter 
     System.out.println("url :" + url);    
     return url;         // find first 
    } 
} 

risposta

3

Il sorted operazione forze attraversamento di tutti gli elementi nel flusso.

Le operazioni con stato, come distinte e ordinate, possono includere lo stato dagli elementi visti in precedenza durante l'elaborazione di nuovi elementi.

Le operazioni di stato possono richiedere l'elaborazione dell'intero input prima che produca un risultato. Ad esempio, non è possibile produrre alcun risultato da ordinando un flusso finché non si sono visti tutti gli elementi del flusso.

(Source)

non sono sicuro, però, il motivo per cui le operazioni seguenti la sorted vengono eseguite anche per tutti gli elementi nel flusso.

Se si esegue l'ordinamento separatamente, quindi si utilizza lo stream per il resto dell'elaborazione, l'elaborazione verrà interrotta quando viene trovata la prima corrispondenza, come previsto.

Arrays.sort(dataArray, Comparator.comparing(d -> d.getPriority())); // sort 

Arrays.stream(dataArray) 
.peek(o -> System.out.println("SORT: " + o)) 
.map(d -> d.getOriginalURL(shortUrl)) 
.peek(o -> System.out.println("MAP: " + o)) 
.filter(u -> u != null && !u.isEmpty()) 
.peek(o -> System.out.println("FILTER: " + o)) 
.findFirst().orElse(""); 
11

Ecco un esempio più piccolo che illustra il problema:

Stream.of("a", "ab", "abc", "abcd") 
    // .sorted() // uncomment and what follows becomes eager 
    .filter(s -> s.contains("b")) 
    .peek(s -> System.out.println("PEEK: " + s)) 
    .findFirst() 
    .orElse("X"); 

Come previsto l'output è:

PEEK: ab 

Se la linea sorted sia commentata, l'output è:

PEEK: ab 
PEEK: abc 
PEEK: abcd 

(Il risultato finale dell'intera pipeline è "ab" in entrambi i casi, come previsto.)

È vero che sorted deve consumare tutto il suo input prima di produrre il suo primo elemento di output. In questo senso è impaziente. Tuttavia, sembra strano che influisce sul modo in cui gli elementi vengono inviati a valle.

Senza l'ordinamento, l'operazione findFirst "estrae" elementi da upstream finché non ne trova uno e quindi si arresta. Con l'ordinamento, l'operazione sorted() raccoglie tutti gli elementi, li ordina e, poiché li ha tutti, li "spinge" verso il basso nello stream. Naturalmente, findFirst ignora tutto tranne il primo elemento. Ma questo significa che le operazioni che intervengono (come il filtro) possono svolgere un lavoro non necessario.

Il risultato finale è corretto, ma il comportamento è inaspettato. Questo potrebbe essere considerato un bug. Indagherò e inoltrerò un bug se appropriato.

+0

Sono d'accordo con te, specialmente se qualcuna delle operazioni di flusso intermedio è un'operazione costosa. (nella mia domanda il 'getOriginalURL' è un costoso! –

+3

Yep, bug delle prestazioni. Archiviato [JDK-8042355] (https://bugs.openjdk.java.net/browse/JDK-8042355). –

+2

@MuhammadHewedy Bella cattura, A proposito, grazie per aver sollevato questo problema –

Problemi correlati