Java 流有条件地添加多个值
Java streams adding multiple values conditionally
我有一个这样的对象列表,其中数量可以是负数或正数:
class Sale {
String country;
BigDecimal amount;
}
最后我想得到一对所有负值和所有正值的总和,按国家/地区分类。
使用这些值:
country | amount
nl | 9
nl | -3
be | 7.9
be | -7
有没有办法使用单个流以 Map<String, Pair<BigDecimal, BigDecimal>>
结束?
用两个独立的流很容易做到这一点,但我无法只用一个。
它应该使用 Collectors.toMap
和合并功能来对对求和。
假设 Pair
是不可变的并且只有第一个和第二个元素的 getter,代码可能如下所示:
static Map<String, Pair<BigDecimal, BigDecimal>> sumUp(List<Sale> list) {
return list.stream()
.collect(Collectors.toMap(
Sale::getCountry,
sale -> sale.getAmount().signum() >= 0
? new Pair<>(sale.getAmount(), BigDecimal.ZERO)
: new Pair<>(BigDecimal.ZERO, sale.getAmount()),
(pair1, pair2) -> new Pair<>(
pair1.getFirst().add(pair2.getFirst()),
pair1.getSecond().add(pair2.getSecond())
)
// , LinkedHashMap::new // optional parameter to keep insertion order
));
}
测试
List<Sale> list = Arrays.asList(
new Sale("us", new BigDecimal(100)),
new Sale("uk", new BigDecimal(-10)),
new Sale("us", new BigDecimal(-50)),
new Sale("us", new BigDecimal(200)),
new Sale("uk", new BigDecimal(333)),
new Sale("uk", new BigDecimal(-70))
);
Map<String, Pair<BigDecimal, BigDecimal>> map = sumUp(list);
map.forEach((country, pair) ->
System.out.printf("%-4s|%s%n%-4s|%s%n",
country, pair.getFirst(), country, pair.getSecond()
));
输出
uk |333
uk |-80
us |300
us |-50
Alex Rudenko 的解决方案,但使用 groupingBy 和下游收集器:
Map<String, Pair<BigDecimal, BigDecimal>> map =
list.stream()
.collect(Collectors.groupingBy(Sale::getCountry,
Collectors.mapping(s ->
s.getAmount().signum() >= 0?
new Pair<>(s.getAmount(), BigDecimal.ZERO):
new Pair<>(BigDecimal.ZERO, s.getAmount()),
Collectors.reducing(new Pair(BigDecimal.ZERO, BigDecimal.ZERO),
(p1, p2) -> new Pair(p1.getKey().add(p2.getKey()),
p1.getValue().add(p2.getValue()))))
));
我有一个这样的对象列表,其中数量可以是负数或正数:
class Sale {
String country;
BigDecimal amount;
}
最后我想得到一对所有负值和所有正值的总和,按国家/地区分类。
使用这些值:
country | amount
nl | 9
nl | -3
be | 7.9
be | -7
有没有办法使用单个流以 Map<String, Pair<BigDecimal, BigDecimal>>
结束?
用两个独立的流很容易做到这一点,但我无法只用一个。
它应该使用 Collectors.toMap
和合并功能来对对求和。
假设 Pair
是不可变的并且只有第一个和第二个元素的 getter,代码可能如下所示:
static Map<String, Pair<BigDecimal, BigDecimal>> sumUp(List<Sale> list) {
return list.stream()
.collect(Collectors.toMap(
Sale::getCountry,
sale -> sale.getAmount().signum() >= 0
? new Pair<>(sale.getAmount(), BigDecimal.ZERO)
: new Pair<>(BigDecimal.ZERO, sale.getAmount()),
(pair1, pair2) -> new Pair<>(
pair1.getFirst().add(pair2.getFirst()),
pair1.getSecond().add(pair2.getSecond())
)
// , LinkedHashMap::new // optional parameter to keep insertion order
));
}
测试
List<Sale> list = Arrays.asList(
new Sale("us", new BigDecimal(100)),
new Sale("uk", new BigDecimal(-10)),
new Sale("us", new BigDecimal(-50)),
new Sale("us", new BigDecimal(200)),
new Sale("uk", new BigDecimal(333)),
new Sale("uk", new BigDecimal(-70))
);
Map<String, Pair<BigDecimal, BigDecimal>> map = sumUp(list);
map.forEach((country, pair) ->
System.out.printf("%-4s|%s%n%-4s|%s%n",
country, pair.getFirst(), country, pair.getSecond()
));
输出
uk |333
uk |-80
us |300
us |-50
Alex Rudenko 的解决方案,但使用 groupingBy 和下游收集器:
Map<String, Pair<BigDecimal, BigDecimal>> map =
list.stream()
.collect(Collectors.groupingBy(Sale::getCountry,
Collectors.mapping(s ->
s.getAmount().signum() >= 0?
new Pair<>(s.getAmount(), BigDecimal.ZERO):
new Pair<>(BigDecimal.ZERO, s.getAmount()),
Collectors.reducing(new Pair(BigDecimal.ZERO, BigDecimal.ZERO),
(p1, p2) -> new Pair(p1.getKey().add(p2.getKey()),
p1.getValue().add(p2.getValue()))))
));