Given the following key...
private class PositionKey {
String assetId;
String currencyId;
}
... I need to create a Map<PositionKey, BigDecimal>
, where if a given key does not exist, then a new entry shall be added, otherwise, if a given key already exists, then its values shall be increased. Here below is my attempt:
public Map<PositionKey, BigDecimal> getMap(final Long portfolioId) {
final Map<PositionKey, BigDecimal> map = new HashMap<>();
// getPositions() returns all the positions associated with the given portfolio
return getPositions(portfolioId).stream()
.collect(
toMap(
position ->
new PositionKey(
position.getAssetId(),
position.geCurrencyId()),
position -> position.geValue(),
(newValue, oldValue) -> oldValue != null ? oldValue.add(newValue) : newValue,
() -> map));
}
What I dislike in my implementation above, is the fact that I create the Map
before streaming the positions. Is there a better way to implement this? Thank you very much.
CodePudding user response:
simple! replace () -> map
with either () -> new HashMap<>()
or HashMap::new
CodePudding user response:
In case if you have no specific requirements regarding the Map
implementation, there's no need to use a version of Collector toMap()
which expects a mapFactory.
Instead, use three-args version, which expects only functions mapping keys/values and resolving duplicates, namely: keyMapper, valueMapper and a mergeFunction toMap(keyMapper, valueMapper, mergeFunction)
.
Also you have messed with the order of arguments in the mergeFunction. The previously assosiated Value comes first, i.e. oldValue
then newValue
(not the opposite). And as @Holger has pointed out in the comments, there's no need to perform null-check unless valueMapper function can produce null
.
(oldValue, newValue) -> oldValue.add(newValue)
Which can be translated into a Method reference:
PositionKey::add
Note that method PositionKey.add()
according to the contract difined by the BinaryOperator
should return an instance of PositionKey
(i.e. you need to return this
).
That's how collect()
operation might look like in this case:
.collect(Collectors.toMap(
position -> new PositionKey(
position.getAssetId(),
position.geCurrencyId()
),
Position::geValue,
PositionKey::add
))
And you would be provided with the default general purpose implementation of the Map
interface by default, which is for now HashMap
(if there would be another implementation which come a recommended replacement of HashMap
you'll get it for free without changing the code). Hence, providing HashMap::new
as a mapFactory is frankly pointless.
A use case for the four-args version of toMap()
would be a situation when you need a special purpose Map
implementation, like EnumMap
, TreeMap
, LinkedHashMap
, etc.