The main point of stream and Optional is that you do not have to actively make an effort to recognize and deal with potential null values. It also promotes immutability of your variables, which makes it easier to reason about things and which reduces the chance of bugs (since you don't need to keep track of where a variable's value was changed last).
BigDecimal total = listOfValues.stream()
.filter(y -> y.isChildrenOf(account))
.map(TotalSsn::getImporte)
.reduce((a, b) -> (a.add(b)))
.orElse(BigDecimal.ZERO);
As you can see there, we don't actually deal with a null value, we just specify the desired default, and as a side-benefit, total does not get reassigned to a different value.
Since reduce also has a starting value, you could also rewrite that to
BigDecimal total = listOfValues.stream()
.filter(y -> y.isChildrenOf(account))
.map(TotalSsn::getImporte)
.reduce(BigDecimal.ZERO, BigDecimal::add);;
Notably, all this is made somewhat more complicated that most use-cases, due to the use of BigDecimal (which of course might be essential in the context). If a regular Double would suffice, the above could be rewritten to
double total = listOfValues.stream()
.filter(y -> y.isChildrenOf(account))
.map(TotalSsn::getImporte)
.sum();
As to orElse vs ifPresent, when you're dealing with default or fallback values, always try to rely on orElse (that's what it's for). The use of ifPresent isn't for when you want to do an assignment, but when you want to only perform an action if there is an actual value resulting from your stream.
myList.stream()
.filter(MyClass::hasCertainTrait)
.map(MyClass::getCertainChildField)
.filter(MyChildClass::isRelevantForMe)
.findFirst()
.ifPresent(childField -> {
// do something useful with childField, like switching the lights on :)
});
Notice here, that I don't need to focus on the case where there isn't a value (i.e. I don't have to ward of a NullPointerException), because the approach side-steps that issue altogether.
Thanks for this explanation!
I have a question on filter/map ect (I'm a JS dev). Do they loop through the whole Collection again and again ? (i.e, the first filter do, then the map do, then the second filter do...).
Hi Daniel,
I'd be glad to elaborate :).
The main point of
stream
andOptional
is that you do not have to actively make an effort to recognize and deal with potential null values. It also promotes immutability of your variables, which makes it easier to reason about things and which reduces the chance of bugs (since you don't need to keep track of where a variable's value was changed last).As you can see there, we don't actually deal with a null value, we just specify the desired default, and as a side-benefit,
total
does not get reassigned to a different value.Since
reduce
also has a starting value, you could also rewrite that toNotably, all this is made somewhat more complicated that most use-cases, due to the use of BigDecimal (which of course might be essential in the context). If a regular
Double
would suffice, the above could be rewritten toAs to
orElse
vsifPresent
, when you're dealing with default or fallback values, always try to rely onorElse
(that's what it's for). The use ofifPresent
isn't for when you want to do an assignment, but when you want to only perform an action if there is an actual value resulting from your stream.Notice here, that I don't need to focus on the case where there isn't a value (i.e. I don't have to ward of a NullPointerException), because the approach side-steps that issue altogether.
Thanks for this explanation!
I have a question on filter/map ect (I'm a JS dev). Do they loop through the whole Collection again and again ? (i.e, the first filter do, then the map do, then the second filter do...).
Alain, thanks for your answer. It's really clear and enriching.
Hi Maxime,
Note that the following is specific to JS. It may work similar or quite different in other languages.
To answer your question, try the following code
This outputs
That shows that in JS, a regular array map operation iterates over the entire array before passing it on to the next operation.