Hi everyone.
This year I worked on learning more Javascript, but I continue to work with Java for my desktop works.
This week I needed to develop a small functionality extending the ERP we work with, to export a text file for compliance with a government agency.
In order to do this, and trying to use a more "functional" approach, I explored the Stream object.
This is a new functionality in the language. A Stream is a collection, that allows data processing declaratively. For instance, I needed to do a sum over an array of items. In the old days of ArrayList, this was my code:
BigDecimal total = BigDecimal.ZERO;
Iterator iterator = listOfValues.iterator();
while (iterator.hasNext()) {
TotalSsn currentElement = iterator.next();
if (currentElement.isChildrenOf(account)) {
total.add(currentElement.getImporte());
}
}
With a little help of the Stream object, the code looks like this:
BigDecimal total = BigDecimal.ZERO;
Optional<BigDecimal> totalOptional = listOfValues.stream().filter(y -> y.isChildrenOf(account)).map(TotalSsn::getImporte).reduce((a, b) -> (a.add(b)));
if (totalOptional.isPresent()) {
total = total.get();
}
In the second approach, I'm using:
stream()
This method returns a stream. It's a method of the List interface, so getting a Stream to work with is pretty straightforward on legacy code.
filter()
This method takes a functional interface as a parameter and returns a new Stream with the results of applying this function to the original stream's elements.
map()
This method allows you to generate a new Stream whith another generic type. In this case I'm converting my TotalSsn to BigDecimal.
reduce()
Allows the calculation, in this case, of a sum of the Stream values.
This whole API, I think, is worthy to be explored.
Saludos,
Top comments (8)
When using streams, the use of 'isPresent' is considered an anti-pattern, because it's just a null-check in disguise. Instead, either use 'ifPresent', or 'orElse', or 'orElseThrow'. Otherwise you're using the tools but missing the point.
Hi Alain. Thanks for your feedback. Could you please elaborate wich point I'm missing so I learn a little more?
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.
Hi Dan,
ifPresent method of reduces/terminal Operators can also be used here.
listOfValues
.stream()
.filter(y -> y.isChildrenOf(account))
.map(TotalSsn::getImporte)
.reduce((a, b) -> (a.add(b)))
.ifPresent(totaloptional -> total = totaloptional);
Thanks
Prasad Chillara
Thanks