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個贊
有沒有辦法在一個流中解決上述任務?
這取決于你所說的“在一個流中”是什么意思。您想要執(zhí)行一個縮減操作,該操作的最佳特征可能是一系列縮減的組合:
按月對訂單進行分組
在每個月組內,匯總每個客戶的訂單以產生總金額
在每個月的每個客戶聚合結果組中,選擇數量最大的一個(注意:在關系的情況下沒有明確定義)
從 Stream API 的角度來看,對流執(zhí)行這些單獨的歸約中的任何一個都是對該流的終端操作。您可以使用新流處理結果,甚至在語法上將其鏈接在一起,但盡管這可能采用單個方法調用鏈的語法形式,但它不會構成在單個流上發(fā)生的所有操作。
您還可以創(chuàng)建一個Collector
(或一個的組件),以便通過收集輸入元素流直接獲得結果,但在內部,該收集器仍需要通過內部創(chuàng)建和消耗額外的來執(zhí)行單獨的歸約流,或通過非流 API 執(zhí)行相同的任務。如果您再次計算這些內部操作,不,它不構成對單個流的執(zhí)行操作。(但是,如果您不考慮這些內部縮減,那么是的,這一切都在一個流中完成。)

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 }}
現在,這有點奇怪,所以讓我們逐行檢查它,看看發(fā)生了什么:
Map<Month, BuyerDetails> grouped = orders.stream().collect(
首先,我們流式傳輸我們的訂單,
Collectors.groupingBy(Order::getOrderMonth,
按月分組,我們發(fā)現:
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())
為所述客戶創(chuàng)建新的買家詳細信息
)
)
)
);
并收集所有 Month/BuyerDetail 對。
System.out.println(grouped);
最后,我們打印創(chuàng)建的數據結構。

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條經驗 獲得超8個贊
這不能用單個流來完成,因為 和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()
)
);
添加回答
舉報