I've got the following class:
class Money {
CurrencyUnit currencyUnit;
BigDecimal amount;
}
In my application, I get some random list of Money
objects:
currencyUnit | amount
---------------------
EUR | 5.1
EUR | 0
USD | 1.09
EUR | 42
USD | 3
Now I'd like to use the Java 8 Stream API to create the following result (simply calling BigDecimal::add
for each currencyUnit's amount):
currencyUnit | amount
---------------------
EUR | 47.1
USD | 4.09
What I already know/did:
Stream<Money> moneyStream = moneyList.stream();
And here it ends already. I know I can use a Collector
to produce a Map<CurrencyUnit, List<Money>>
:
moneyStream.collect(Collectors.groupingBy(m -> m.getCurrencyUnit());
But then I still have to go through all key-value-pairs and sum up the amount data.
What's the right (and possibly easiest way) to do that? It can't be that complicated, right? :)
EDIT: If it's not that clear what I need, here's my old-way Java code:
Map<CurrencyUnit, Money> map = new HashMap<>();
moneyList.stream().forEach(e -> {
Money m = map.get(e.getCurrencyUnit());
if(m == null) {
m = new Money();
m.setAmount(BigDecimal.ZERO);
m.setCurrencyUnit(e.getCurrencyUnit());
map.put(e.getCurrencyUnit(), m);
}
m.setAmount(m.getAmount().add(e.getAmount()));
});
return map.values();
EDIT 2: Another solution, which isn't really elegant:
List<Money> list = inputList.stream()
.collect(Collectors.groupingBy(Money::getCurrencyUnit))
.values().stream().map(ml -> {
Money money = new Money();
ml.forEach(m -> {
if(money.getCurrencyUnit() == null) {
money.setCurrencyUnit(m.getCurrencyUnit());
money.setAmount(m.getAmount());
} else {
money.setAmount(money.getAmount().add(m.getAmount()));
}
});
return money;
}).collect(Collectors.toList());
Aggregate operations − Stream supports aggregate operations like filter, map, limit, reduce, find, match, and so on. Pipelining − Most of the stream operations return stream itself so that their result can be pipelined.
Using Collectors. Get the Stream to be converted. Collect the stream as List using collect() and Collectors. toList() methods. Convert this List into an ArrayList.
All you need to do is first get the stream from List by calling stream() method, then call the filter() method to create a new Stream of filtered values and finally call the Collectors. toCollection(ArrayList::new) to collect those elements into an ArrayList.
You can use a groupingBy
collector to group the objects by CurrencyUnit
. Without second argument, the groupingBy
method collects the elements into a list. However, you can also specify a downstream collector if you need something else.
You can use Collectors::summingInt
and Collectors::summingLong
for int
and long
. For BigDecimal
, you can fall back to Collectors::reducing
:
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.reducing;
Map<CurrencyUnit, BigDecimal> result = moneyList.stream()
.collect(
groupingBy(
Money::getCurrencyUnit,
reducing(BigDecimal.ZERO, Money::getAmount, BigDecimal::add)));
Edit: You can also create List<Money>
:
List<Money> result = moneyList.stream()
.collect(
groupingBy(
Money::getCurrencyUnit,
reducing(BigDecimal.ZERO, Money::getAmount, BigDecimal::add)))
.entrySet().stream()
.map(e -> new Money(e.getKey(), e.getValue())
.collect(toList());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With