You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Java Stream将一种类型集合归约为另一种类型的单个对象

用Java Stream的reduce实现类型归约

我来帮你搞定这个Stream归约的问题!首先,你原来用forEach修改外部对象的方式虽然能跑,但确实不符合函数式编程的风格,用reduce来实现是更优雅的选择,不过得注意正确的用法,尤其是避免副作用和线程安全问题。

推荐的纯函数式实现(无副作用,支持并行流)

@Test
public void streams() {
    List<MyData> collection = Arrays.asList(
            new MyData("1", "cool"),
            new MyData("2", "green"),
            new MyData("3", "night")
    );

    MyResult result = collection.stream()
            .reduce(new MyResult(), // 初始值:创建一个空的MyResult
                    (currentRes, data) -> {
                        // 找到目标元素时,返回一个新的MyResult对象,避免修改原对象
                        if ("2".equals(data.key)) {
                            MyResult updatedRes = new MyResult();
                            updatedRes.color = data.value;
                            // 如果MyResult还有其他字段,记得复制currentRes里的原有值
                            // updatedRes.otherField = currentRes.otherField;
                            return updatedRes;
                        }
                        // 没找到的话,直接返回当前结果
                        return currentRes;
                    },
                    // 并行流时的组合器:合并两个结果,这里我们取有color值的那个
                    (res1, res2) -> res2.color != null ? res2 : res1);

    // 验证结果
    MyResult expectedResult = new MyResult();
    expectedResult.color = "green";
    assertEquals(expectedResult.color, result.color);
}

为什么要返回新对象?

函数式编程里推荐避免修改传入的参数(也就是currentRes),因为如果使用并行流的话,多个线程会同时操作同一个对象,导致线程安全问题。返回新对象能保证每个累加步骤都是独立的,并行流也能安全运行。

简化版(串行流可用,不推荐并行)

如果你的场景肯定是串行流,也可以直接修改传入的结果对象,但这种方式有副作用,不推荐在并行流中使用:

MyResult result = collection.stream()
        .reduce(new MyResult(),
                (res, data) -> {
                    if ("2".equals(data.key)) {
                        res.color = data.value;
                    }
                    return res;
                },
                (res1, res2) -> {
                    // 并行时合并结果,这里取第二个结果的color(如果有的话)
                    if (res2.color != null) {
                        res1.color = res2.color;
                    }
                    return res1;
                });

更语义化的替代方案

其实你的需求更偏向于查找单个元素并转换,用filter+findFirst+map会比reduce更直观,语义也更清晰:

MyResult result = collection.stream()
        .filter(data -> "2".equals(data.key)) // 过滤出key为"2"的元素
        .findFirst() // 获取第一个匹配的元素(你的例子里只有一个)
        .map(data -> { // 转换为MyResult对象
            MyResult res = new MyResult();
            res.color = data.value;
            return res;
        })
        .orElse(new MyResult()); // 如果没找到匹配元素,返回默认的MyResult

这种方式代码更易读,也符合你的实际需求场景——毕竟reduce更多用于聚合多个元素的信息到一个结果中,而不是查找单个元素。

内容的提问来源于stack exchange,提问作者sschmeck

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 07:06:36