我正在探索 Java Stream Gatherers,这是 Java 22 中引入的预览功能,并且我正在使用 Java 23,并为此目的启用了预览功能。我的主要目标是更好地理解 Gatherer API 的工作原理,而不仅仅是解决“按产品代码区分”问题。
在我的代码中,我希望根据产品代码收集三个不同的 Offer 对象,但我始终只得到 2 个。调试显示收集器总是忽略流中的最后一项。当我将优惠数量增加到 4 时,结果大小变为 3,但它仍然跳过最后一项。
我不确定我是否误解了 Gatherer API 的工作原理,或者此预览功能的 JDK 实现中是否可能存在错误。
相关代码如下:
package com.mycompany.app;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Gatherer;
public class AppTest {
@Test
void exploreStreamGatherer() {
Offer grandChildOffer = new Offer("GP1", List.of());
Offer grandChildOffer2 = new Offer("GP1", List.of());
Offer grandChildOffer3 = new Offer("GP2", List.of());
Offer grandChildOffer4 = new Offer("GP3", List.of());
Offer childOffer1 = new Offer("CP1", List.of(grandChildOffer, grandChildOffer4));
Offer childOffer2 = new Offer("CP2", List.of(grandChildOffer2, grandChildOffer3));
Offer offer = new Offer("P1", List.of(childOffer1, childOffer2));
List<Offer> distinctGrandChildOffers = getAllDistinctGrandChildOffersByProductCode(offer);
assertEquals(3, distinctGrandChildOffers.size()); // Fails, returns 2
}
static List<Offer> getAllDistinctGrandChildOffersByProductCode(Offer offer) {
return offer.childOffers().stream()
.flatMap(childOffer -> childOffer.childOffers().stream())
.gather(distinctByProductCode())
.toList();
}
public static Gatherer<Offer, List<Offer>, Offer> distinctByProductCode() {
return Gatherer.ofSequential(
ArrayList::new,
(state, element, downstream) -> {
if (hasProductWithSameProductCode(state, element)) {
return false;
}
state.add(element);
return true;
},
(state, downstream) -> {
if (!state.isEmpty()) {
state.forEach(downstream::push);
}
}
);
}
private static boolean hasProductWithSameProductCode(List<Offer> state, Offer element) {
return state.stream().anyMatch(offer -> offer.productCode().equals(element.productCode()));
}
}
record Offer(String productCode, List<Offer> childOffers) {
}
有关完整上下文,可在此处获取项目源代码:GitHub 存储库。
主要焦点:
目标是探索和理解 Gatherer API 在 Java 22/23 中的工作原理,而不仅仅是解决“按产品代码区分”逻辑。
当我期望 3 件物品时,收集器仅返回 2 件。如果我将报价数量增加到 4 件,我会得到 3 件物品,但最后一件仍然被省略。
问题:
返回的
false
:
if (hasProductWithSameProductCode(state, element)) {
return false;
}
停止从流中处理更多项目,因此从那时起您将跳过所有内容。您应该返回
true
以继续集成剩余的流元素。
由于您想要处理所有流元素,您可以使用
Greedy
积分器,它允许收集器进行一些优化:
public static Gatherer<Offer, List<Offer>, Offer> distinctByProductCode() {
return Gatherer.ofSequential(
ArrayList::new,
Gatherer.Integrator.<List<Offer>, Offer, Offer>ofGreedy((state, element, downstream) -> {
if (!hasProductWithSameProductCode(state, element)) {
state.add(element);
}
return true;
}),
(state, downstream) -> {
if (!state.isEmpty()) {
state.forEach(downstream::push);
}
}
);
}