Java 22 Stream Gatherer 预览功能中的意外行为:省略了 Stream 中的最后一项

问题描述 投票:0回答:1

我正在探索 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 件物品,但最后一件仍然被省略。

问题:

  1. Gatherer API 处理流的方式是否有我遗漏的地方?
  2. 这可能是预览功能实现中的错误,还是我滥用了 API?
  3. 关于 Gatherer 是否有任何最佳实践或已知限制可以解释这种行为?
java java-stream java-22 java-23
1个回答
0
投票

返回的

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);
                }
            }
    );
}
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.