如何将 List<Map<Customer, Map<key, BigDecimal>>> 转换为 Map<Customer, Map<key, BigDecimal>>
how to transform a List<Map<Customer, Map<key, BigDecimal>>> into a Map<Customer, Map<key, BigDecimal>>
我遇到了 java8 流问题。
我有一个
List<Map<Customer, Map<key, BigDecimal>>>
我想变成
Map<Customer, Map<key, BigDecimal>>
其中键是月份,BigDecimal 是 BigDecimals 的总和。
我可以通过迭代来完成,但我想使用 java8 流,因为我认为这应该是可能的。
当我迭代时,我使用的是临时
Map<Customer, List<Map<key, BigDecimal>>>
我通过对每个客户的迭代来减少。
但是要用流来做,我不得不将元素添加到临时列表中!
有人可以帮忙吗?
在 java 9 中,我们将获得 flatMapping
收集器 (see here),这将使这类事情变得更简单。
您可以将 flatMapping
的代码反向移植到您的项目中,当 java 9 次命中时,将静态导入替换为官方导入。它看起来像这样:
public static <T, U, A, R>
Collector<T, ?, R> flatMapping(
Function<? super T, ? extends Stream<? extends U>> mapper,
Collector<? super U, A, R> downstream
) {
BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();
return Collector.of(
downstream.supplier(),
(r, t) -> {
try (Stream<? extends U> result = mapper.apply(t)) {
if (result != null) {
result.sequential()
.forEach(u -> downstreamAccumulator.accept(r, u));
}
}
},
downstream.combiner(), downstream.finisher(),
downstream.characteristics().toArray(new Collector.Characteristics[0])
);
}
然后你会像这样使用它:
Map<Customer, Map<key, BigDecimal>> result = input.stream()
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(groupingBy(
e->e.getKey(),
flatMapping(
e->e.getValue().entrySet().stream(),
toMap(e->e.getKey(), e->e.getValue(), BigDecimal::add)
)
));
如果您不想这样做并且更愿意使用当前标准的收集器,您可以使用 3 参数 toMap
,它结合了 map 和 reduce:
Map<Customer, Map<key, BigDecimal>> result = input.stream()
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(toMap(
e->e.getKey(),
e->e.getValue(),
(a,b) -> Stream.of(a,b) // merge the 2 maps
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(toMap(
e->e.getKey(),
e->e.getValue(),
BigDecimal::add
))
))
API 中缺少的是合并两个完整 Map
的函数(尽管存在合并单个映射的方法)。使用一些辅助函数来创建这样的 Collector
static <K,V> Collector<Map<K,V>,?,Map<K,V>> mergeMaps(BinaryOperator<V> op) {
return Collector.of(()->new HashMap<>(),
(m,n)-> n.forEach((k,v)->m.merge(k, v, op)),
(m,n)->{ n.forEach((k,v)->m.merge(k, v, op)); return m; });
}
你可以像
一样解决你的任务
Map<Customer, Map<Key, BigDecimal>> collected =
list.stream().flatMap(m->m.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, mergeMaps(BigDecimal::add))));
我可以通过
来排序
private static final BinaryOperator<FactureMensuel> applatirListeFactureMensuelle = new BinaryOperator<FactureMensuel>() {
@Override
public FactureMensuel apply(FactureMensuel factureMensuel, FactureMensuel factureMensuel2) {
factureMensuel2.forEach((k, v) -> factureMensuel.merge(k, v, BigDecimal::add));
return factureMensuel;
}
};
oemInvoices.stream()
.reduce((oemInvoice1, oemInvoice2) -> {
oemInvoice2.forEach((k, v) -> oemInvoice1.merge(k, v, applatirListeFactureMensuelle));
return oemInvoice1;
}).get();
在地图上使用 forEach 合并两个地图
我遇到了 java8 流问题。
我有一个
List<Map<Customer, Map<key, BigDecimal>>>
我想变成
Map<Customer, Map<key, BigDecimal>>
其中键是月份,BigDecimal 是 BigDecimals 的总和。
我可以通过迭代来完成,但我想使用 java8 流,因为我认为这应该是可能的。
当我迭代时,我使用的是临时
Map<Customer, List<Map<key, BigDecimal>>>
我通过对每个客户的迭代来减少。
但是要用流来做,我不得不将元素添加到临时列表中!
有人可以帮忙吗?
在 java 9 中,我们将获得 flatMapping
收集器 (see here),这将使这类事情变得更简单。
您可以将 flatMapping
的代码反向移植到您的项目中,当 java 9 次命中时,将静态导入替换为官方导入。它看起来像这样:
public static <T, U, A, R>
Collector<T, ?, R> flatMapping(
Function<? super T, ? extends Stream<? extends U>> mapper,
Collector<? super U, A, R> downstream
) {
BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();
return Collector.of(
downstream.supplier(),
(r, t) -> {
try (Stream<? extends U> result = mapper.apply(t)) {
if (result != null) {
result.sequential()
.forEach(u -> downstreamAccumulator.accept(r, u));
}
}
},
downstream.combiner(), downstream.finisher(),
downstream.characteristics().toArray(new Collector.Characteristics[0])
);
}
然后你会像这样使用它:
Map<Customer, Map<key, BigDecimal>> result = input.stream()
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(groupingBy(
e->e.getKey(),
flatMapping(
e->e.getValue().entrySet().stream(),
toMap(e->e.getKey(), e->e.getValue(), BigDecimal::add)
)
));
如果您不想这样做并且更愿意使用当前标准的收集器,您可以使用 3 参数 toMap
,它结合了 map 和 reduce:
Map<Customer, Map<key, BigDecimal>> result = input.stream()
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(toMap(
e->e.getKey(),
e->e.getValue(),
(a,b) -> Stream.of(a,b) // merge the 2 maps
.map(Map::entrySet)
.flatMap(Set::stream)
.collect(toMap(
e->e.getKey(),
e->e.getValue(),
BigDecimal::add
))
))
API 中缺少的是合并两个完整 Map
的函数(尽管存在合并单个映射的方法)。使用一些辅助函数来创建这样的 Collector
static <K,V> Collector<Map<K,V>,?,Map<K,V>> mergeMaps(BinaryOperator<V> op) {
return Collector.of(()->new HashMap<>(),
(m,n)-> n.forEach((k,v)->m.merge(k, v, op)),
(m,n)->{ n.forEach((k,v)->m.merge(k, v, op)); return m; });
}
你可以像
一样解决你的任务Map<Customer, Map<Key, BigDecimal>> collected =
list.stream().flatMap(m->m.entrySet().stream())
.collect(Collectors.groupingBy(Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue, mergeMaps(BigDecimal::add))));
我可以通过
来排序 private static final BinaryOperator<FactureMensuel> applatirListeFactureMensuelle = new BinaryOperator<FactureMensuel>() {
@Override
public FactureMensuel apply(FactureMensuel factureMensuel, FactureMensuel factureMensuel2) {
factureMensuel2.forEach((k, v) -> factureMensuel.merge(k, v, BigDecimal::add));
return factureMensuel;
}
};
oemInvoices.stream()
.reduce((oemInvoice1, oemInvoice2) -> {
oemInvoice2.forEach((k, v) -> oemInvoice1.merge(k, v, applatirListeFactureMensuelle));
return oemInvoice1;
}).get();
在地图上使用 forEach 合并两个地图