2014-11-25 12 views
11

Sono nuovo di programmazione funzionale in Java, e mi chiedo come dovrei codice per evitare NPE a (per esempio) questa operazione:.flusso Calling() ridurre() su una lista con un solo elemento

myList.stream() 
     .reduce((prev, curr) -> prev.getTimestamp().isAfter(curr.getTimestamp()) ? prev : curr); 
     .get().getTimestamp(); 

Il mio intento qui è quello di trovare il timestamp del nuovo oggetto nella lista. I suggerimenti su come raccogliere meglio l'ultimo elemento sono i benvenuti, ma la mia domanda principale qui è in realtà perché funziona.

La documentazione dice che la funzione getta una NullPointerException "se il risultato della riduzione è nullo":

http://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#reduce-java.util.function.BinaryOperator-

che è ok, ma quello che non capisco è il motivo per I don' t ottenere NullPointerException quando questo codice viene eseguito con un elenco contenente solo un elemento. Mi aspettavo che lo prev fosse nullo in tal caso. Ho provato il debug, ma sembra proprio scavalcare l'intera espressione lambda quando c'è un solo elemento.

risposta

15

Come JavaDoc di reduce dice, ridurre equivale a:

boolean foundAny = false; 
T result = null; 
for (T element : this stream) { 
    if (!foundAny) { 
     foundAny = true; 
     result = element; 
    } 
    else 
     result = accumulator.apply(result, element); 
} 
return foundAny ? Optional.of(result) : Optional.empty(); 

Pertanto, se il flusso è un singolo elemento, c'è solo un'iterazione del ciclo, e il singolo elemento trovato in quella iterazione viene restituito.

Lo BinaryOperator verrà applicato solo se lo Stream ha almeno due elementi.

1

NPE verrà generato se l'operazione di riduzione risulta in un valore null. Ad esempio se provi a fare stream.reduce((a,b) -> null).

In un'altra nota, per trovare il nuovo timestamp, si può fare questo:

myList.stream() 
    .map(t -> t.getTimeStamp()) 
    .max(Comparator.naturalOrder() 
    .get(); // will throw an exception if stream is empty 

Oppure, con l'importazione statica e supponendo che il classe si chiama Thing:

myList.stream().map(Thing::getTimeStamp).max(naturalOrder()).get(); 
+0

Si noti che questo restituirà solo il timestamp più recente e non l'oggetto con il timestamp più recente. – skiwi

+0

Grazie, ne sono consapevole e il timestamp è quello che voglio. – aweibell

5

Meglio:

Optional<Thingie> max = 
     myList.stream() 
      .reduce(BinaryOperator.maxBy(comparing(Thingie::getTimeStamp)); 

Questo sovraccarico di reduce() restituisce un Opzionale; sballare ciecamente con get è pericoloso e rischia di lanciare NSEE. Disimballalo con uno degli operatori sicuri come orElse o orElseThrow.

Se è possibile che nel flusso siano presenti valori null, filtrare prima con .filter(t -> t != null).

+0

Grazie. Ho già verificato che lo stream non è vuoto e non penso che potrebbero esserci elementi nulli, ma immagino che il tuo approccio sarà sempre migliore. Lo guarderò domani. – aweibell

Problemi correlati