[英]Java8 Stream how to groupingBy and combine elements with same fields
这个问题的核心可能是如何创建一个 Collector 来组合具有相同 ID 和日期的订单,结果是一个订单列表而不是一个 Map<Integer, List>。
还有其他方法可以简化此流程吗?
public class Order {
private Integer id;
private LocalDate date;
private double amount;
public void accept(Order other) {
setId(other.getId());
setDate(other.getDate());
setAmount(getAmount() + other.getAmount());
}
public Order combine(Order other) {
setId(other.getId());
setDate(other.getDate());
setAmount(getAmount() + other.getAmount());
return this;
}
}
List<Order> result = new ArrayList<>();
List<Order> orders = mockData();
Map<Integer, List<Order>> collect = list.stream()
.collect(groupingBy(Order::getId));
collect.forEach((id, orders) -> {
Map<LocalDate, Order> resultMap = orders.stream()
.collect(groupingBy(Order::getDate, mapping(order -> order, Collector.of(Order::new, Order::accept, Order::combine))));
result.addAll(resultMap.values());
});
我会先创建一个新记录,以便您可以同时使用 id 和 date。
record IdAndDate(Integer id, LocalDate date) {}
要不获取List<Order>
作为地图的值类型,请使用toMap
收集器。 然后您可以指定一个“合并功能”。 那就是您指定Order::combine
的地方。
var result = new ArrayList<>(
orders.stream().collect(
Collectors.toMap(
x -> new IdAndDate(x.getId(), x.getDate()), // key mapper
Function.identity(), // value mapper (no change)
Order::combine // merge function
)
).values()
);
请注意, Order.combine
更改了调用它的实例,这意味着原始列表中的某些订单将被此操作更改。 这对于您的原始代码也是如此,所以我假设这个事实对您的情况无关紧要。 以防万一您不希望这种情况发生,您应该让combine
返回一个新的Order
实例,而不是this
。
您还可以嵌套 groupingBy 调用并执行以下操作:
List<Order> orders = mockData();
List<Order> result = orders.stream() // Stream<Order>
.collect(Collectors.groupingBy(Order::getId,
Collectors.groupingBy(Order::getDate))) //Map<Integer, Map<LocalDate,List<Order>>>
.values() //Collection<Map<LocalDate,List<Order>>>
.stream() ////Stream<Map<LocalDate,List<Order>>>
.flatMap(m -> m.values().stream()) //Stream<List<Order>>
.map(list -> list.stream().reduce(Order::combine).get()) // Stream<Order>
.collect(Collectors.toList());
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.