6 回答
TA贡献1906条经验 获得超3个赞
尝试使用groupingBy,summingLong如下comparingLong所示
Map<Month, BuyerDetails> topBuyers = orders.stream()
.collect(Collectors.groupingBy(Order::getOrderMonth,
Collectors.groupingBy(Order::getCustomer,
Collectors.summingLong(Order::getAmount))))
.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey,
order -> order.getValue().entrySet().stream()
.max(Comparator.comparingLong(Map.Entry::getValue))
.map(cust -> new BuyerDetails(cust.getKey(), cust.getValue())).get()));
输出
{
"MARCH": { "customer": "Dan", "amount": 300 },
"APRIL": { "customer": "Jenny", "amount": 550 }
}
TA贡献1775条经验 获得超11个赞
有没有办法在一个流中解决上述任务?
这取决于你所说的“在一个流中”是什么意思。您想要执行一个缩减操作,该操作的最佳特征可能是一系列缩减的组合:
按月对订单进行分组
在每个月组内,汇总每个客户的订单以产生总金额
在每个月的每个客户聚合结果组中,选择数量最大的一个(注意:在关系的情况下没有明确定义)
从 Stream API 的角度来看,对流执行这些单独的归约中的任何一个都是对该流的终端操作。您可以使用新流处理结果,甚至在语法上将其链接在一起,但尽管这可能采用单个方法调用链的语法形式,但它不会构成在单个流上发生的所有操作。
您还可以创建一个Collector
(或一个的组件),以便通过收集输入元素流直接获得结果,但在内部,该收集器仍需要通过内部创建和消耗额外的来执行单独的归约流,或通过非流 API 执行相同的任务。如果您再次计算这些内部操作,不,它不构成对单个流的执行操作。(但是,如果您不考虑这些内部缩减,那么是的,这一切都在一个流中完成。)
TA贡献1757条经验 获得超7个赞
好吧,我们开始吧!以下代码将为您提供所需的内容,只需调用 1 次即可stream():
Map<Month, BuyerDetails> grouped = orders.stream().collect(
Collectors.groupingBy(Order::getOrderMonth,
Collectors.collectingAndThen(
Collectors.groupingBy(Order::getCustomer,
Collectors.summingLong(Order::getAmount)
),
((Function<Map<String,Long>, Map.Entry<String,Long>>)
map -> Collections.max(
map.entrySet(), Comparator.comparingLong(Map.Entry::getValue)
)
).andThen(
e -> new BuyerDetails(e.getKey(),e.getValue())
)
)
)
);
System.out.println(grouped);
输出:
{MARCH={ customer='Dan', amount=300 }, APRIL={ customer='Jenny', amount=550 }}
现在,这有点奇怪,所以让我们逐行检查它,看看发生了什么:
Map<Month, BuyerDetails> grouped = orders.stream().collect(
首先,我们流式传输我们的订单,
Collectors.groupingBy(Order::getOrderMonth,
按月分组,我们发现:
Collectors.collectingAndThen(
Collectors.groupingBy(Order::getCustomer,
每个客户和
Collectors.summingLong(Order::getAmount)
),
他们一个月内的总订单。
((Function<Map<String,Long>, Map.Entry<String,Long>>)
(我们转换为Function,所以我们可以使用andThen我们定义的 lambda 函数之类的方法)
map -> Collections.max(
map.entrySet(), Comparator.comparingLong(Map.Entry::getValue)
)
对于每个月,我们都会找到具有最大订单金额的客户。
).andThen(
然后我们
e -> new BuyerDetails(e.getKey(),e.getValue())
为所述客户创建新的买家详细信息
)
)
)
);
并收集所有 Month/BuyerDetail 对。
System.out.println(grouped);
最后,我们打印创建的数据结构。
TA贡献1783条经验 获得超4个赞
它有一个嵌套流,所以它不是一个流,它返回Map<String, Optional<BuyerDetails>>。
orders.stream()
.collect(
Collectors.groupingBy(Order::getOrderMonth,
Collectors.collectingAndThen(
Collectors.groupingBy(
Order::getCustomer,
Collectors.summarizingLong(Order::getAmount)
),
e -> e.entrySet()
.stream()
.map(entry -> new BuyerDetails(entry.getKey(), entry.getValue().getSum()))
.max(Comparator.comparingLong(BuyerDetails::getAmount))
)
)
)
所以有3个步骤:
按月分组Collectors.groupingBy(Order::getOrderMonth,
按客户名称分组并汇总总订单金额Collectors.groupingBy(Order::getCustomer, Collectors.summarizingLong( Order::getAmount))
过滤中间结果,只留下最大金额的客户max(Comparator.comparingLong(BuyerDetails::getAmount))
输出是
{
APRIL = Optional [ BuyerDetails { customer = 'Jenny', amount = 550 } ],
MARCH = Optional [ BuyerDetails { customer = 'Dan', amount = 300 } ]
}
我也很好奇这是否可以在没有额外流的情况下完成。
TA贡献1775条经验 获得超8个赞
我的方法(3 个流):
private static void solution1(List<Order> orders) {
final Map<Month, BuyerDetails> topBuyers = orders.stream().collect(
Collectors.groupingBy(order -> order.getCustomer() + "$" + order.getOrderDate().getYear() + "." +
order.getOrderMonth(),
Collectors.reducing((ord1, ord2) -> {
ord1.setAmount(ord1.getAmount() + ord2.getAmount());
return ord1;
}))).values().stream()
.collect(Collectors.groupingBy(order -> order.get().getOrderMonth(),
maxBy(Comparator.comparing(order -> order.get().getAmount())))).values().stream()
.collect(
toMap((key) -> key.get().get().getOrderMonth(),
key -> new BuyerDetails(key.get().get().getCustomer(), key.get().get().getAmount())
)
);
}
TA贡献2039条经验 获得超7个赞
这不能用单个流来完成,因为 和sum都是max终端操作,它们不能应用于同一个流。最好把它分成两个操作
Map<Month, Map<String, Long>> sumsByMonth = orders.stream().collect(
Collectors.groupingBy(
Order::getOrderMonth,
Collectors.groupingBy(
Order::getCustomer,
Collectors.mapping(
Order::getAmount,
Collectors.reducing(0L, a -> a, (a1, a2) -> a1 + a2)
)
)
)
);
Map<Month, BuyerDetails> topBuyers = sumsByMonth.entrySet().stream().collect(
Collectors.toMap(
Map.Entry::getKey,
sums -> sums.getValue().entrySet().stream()
.max(Comparator.comparingLong(Map.Entry::getValue))
.map(e -> new BuyerDetails(e.getKey(), e.getValue()))
.get()
)
);
添加回答
举报