我拥有以下域类的类:
@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);