收集器映射()+还原()-折叠与Java 8具有一对多关系的流元素



我拥有以下域类的类:

@AllArgsConstructor
@Getter
@ToString
public static class Customer {
private String id;
private String name;
}
@Getter
public static class Order {
private String id;
private Set<OrderContent> orderContent = new HashSet<>();
private Customer customer;

public Order(String id, Customer customer) {
this.id = id;
this.customer = customer;
}

public Order addOrderContent(Product product, int amount) {
orderContent.add(new OrderContent(product, amount));
return this;
}
}
@AllArgsConstructor
@Getter
public static class Product {
private String name;
private BigDecimal price;
}
@AllArgsConstructor
@Getter
public static class OrderContent {
private Product product;
private Integer quantity;
}

因此,每个Order与一个Customer相关联,并且与OrderContent进行一对多关联。每个OrderContent都具有属性:乘积数量

我想退回最有价值的客户,即在所有订单中价值最高的客户。

让我们考虑下面的例子:

List<Order> orders = new ArrayList<>();
Customer customer1 = new Customer("1", "Bill");
Customer customer2 = new Customer("2", "John");
Product device1 = new Product("Device 1", new BigDecimal("30.00"));
Product device2 = new Product("Device 2", new BigDecimal("50.00"));
Product device3 = new Product("Device 3", new BigDecimal("110.00"));
orders.add(new Order("1", customer1).addOrderContent(device1 , 1));
orders.add(new Order("2", customer1).addOrderContent(device2 , 1));
orders.add(new Order("3", customer2).addOrderContent(device3, 1));

以下代码编译失败。问题位于收集器内部,因为mapping()内部的参数类型不匹配。如何正确地实现这个逻辑?

我的代码:

Customer customer = orders.stream()
.collect(Collectors.groupingBy(Order::getCustomer,
Collectors.mapping((Order order) -> order.getOrderContent().stream()
.map(orderContent -> orderContent.getProduct().getPrice()
.multiply(new BigDecimal(orderContent.getQuantity()))),
Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))
))
.entrySet().stream()
.max(Comparator.comparing(Map.Entry::getValue))
.map(Map.Entry::getKey).orElse(null);

你差不多到了。

唯一需要的是正确处理一对多转换-将订单转换为命令中的产品价格。

Java 9+

您可以使用collectorflatMapping()作为groupingBy()的下游,而不是使用collectormapping()Order对象转换为一系列OrderContent对象。

然后,我们可以通过乘以价格和金额将每个OrderContent转化为BigDecimal,并像您一样使用reducing()获得每个Customer的总花费。

Customer mostValCust = orders.stream()
.collect(Collectors.groupingBy(Order::getCustomer,
Collectors.flatMapping((Order order) -> order.getOrderContent().stream()
.map(orderContent -> orderContent.getProduct().getPrice()
.multiply(BigDecimal.valueOf(orderContent.getQuantity()))),
Collectors.reducing(BigDecimal.ZERO, BigDecimal::add))
))
.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(null);

Java 8

使用Java 8实现这一点的方法之一是将用于累积每个Order的总价格的逻辑移动到收集器reducing():

Customer mostValCust = orders.stream()
.collect(Collectors.groupingBy(Order::getCustomer,
Collectors.reducing(
BigDecimal.ZERO,
order -> order.getOrderContent().stream()
.map(orderContent -> orderContent.getProduct().getPrice()
.multiply(BigDecimal.valueOf(orderContent.getQuantity())))
.reduce(BigDecimal.ZERO,BigDecimal::add),
BigDecimal::add)
))
.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(null);

最新更新