Una volta filtrati dallo stream, non c'è modo di sapere se tutti i saldi sono stati null
(a meno che non verifichi cosa restituisce count()
ma non sarà possibile utilizzare lo streaming poiché si tratta di un'operazione terminale).
Facendo due passaggi sui dati è probabilmente la soluzione straight-forward, e mi sarebbe probabilmente andare con quel primo:
boolean allNulls = account.stream().map(Account::getBalance).allMatch(Objects::isNull);
Long sum = allNulls ? null : account.stream().map(Account::getBalance).filter(Objects::nonNull).mapToLong(l -> l).sum();
Si potrebbe eliminare il passaggio filtrante con la vostra soluzione con reduce
, anche se il leggibilità forse non essere il migliore:
Long sum = account.stream()
.reduce(null, (l1, l2) -> l1 == null ? l2 :
l2 == null ? l1 : Long.valueOf(l1 + l2));
Avviso del Long.valueOf
chiamata. È per evitare che il tipo di espressione condizionale sia long
e quindi un NPE su alcuni casi limite.
Un'altra soluzione sarebbe utilizzare l'API
Optional
.In primo luogo, creare una
Stream<Optional<Long>>
dai valori dei saldi e ridurli:
Optional<Long> opt = account.stream()
.map(Account::getBalance)
.flatMap(l -> Stream.of(Optional.ofNullable(l)))
.reduce(Optional.empty(),
(o1, o2) -> o1.isPresent() ? o1.map(l -> l + o2.orElse(0L)) : o2);
Questo vi darà un Optional<Long>
che sarà vuota se tutti i valori erano null
, altrimenti ti do la somma della non valori null.
Oppure si potrebbe desiderare di creare una collezione personalizzata per questo:
class SumIntoOptional {
private boolean allNull = true;
private long sum = 0L;
public SumIntoOptional() {}
public void add(Long value) {
if(value != null) {
allNull = false;
sum += value;
}
}
public void merge(SumIntoOptional other) {
if(!other.allNull) {
allNull = false;
sum += other.sum;
}
}
public OptionalLong getSum() {
return allNull ? OptionalLong.empty() : OptionalLong.of(sum);
}
}
e poi:
OptionalLong opt = account.stream().map(Account::getBalance).collect(SumIntoOptional::new, SumIntoOptional::add, SumIntoOptional::merge).getSum();
Come si può vedere, ci sono vari modi per raggiungere questo obiettivo, quindi la mia consiglio sarebbe: scegliere prima il più leggibile. Se si verificano problemi di prestazioni con la soluzione, controllare se potrebbe essere migliorata (sia ruotando il flusso in parallelo o utilizzando un'altra alternativa). Ma misura, non indovinare.
È meglio suddividerlo in 2 righe, una per filtrare il 'nullo' e un altro per restituire 'null' se la dimensione è' 0', somma altrimenti. È possibile farlo in una riga usando 'reduce' piuttosto che' filtro', ma la leggibilità ne risentirebbe. –
In realtà è una cattiva pratica restituire null (vedi NullObject Pattern) ... – Maksym
@pbabcdefp - ah, grazie. Sì, l'ho rielaborato in due righe come suggerisci, ed è sicuramente più leggibile. – user384842