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

Java Stream使用Collectors.reducing累加Double值时遇空指针异常的解决

问题:Author列表聚合生成Map时的NullPointerException解决及空值检查

我有一个Author对象列表,需要生成Map<String, Author>,键是transId与countryCode的拼接字符串,值是聚合后的Author对象——同一组(transId和countryCode相同)对象的currencyUSD和exccessAmount分别求和。

示例数据

Author a1 = new Author("1111111", "US", 1000.00, 50.0);
Author a2 = new Author("1111111", "US", 1000.00, 50.0);
Author a3 = new Author("1111111", "US", 1000.00, 1000.00);
Author a4 = new Author("222222", "US", 1000.00, 1000.00);
Author a5 = new Author("222222", "US", 1000.00, 1000.00);
Author a6 = new Author("3333333", "US", 1000.00, 1000.00);
Author a7 = new Author("444444", "US", 1000.00, 1000.00);
        
List<Author> authors = List.of(
    a1, a2, a3, a4, a5, a6, a7
);

编写的代码

public static void prepareList() {

    List<Author> authors = // 初始化列表

    Map<String, Author> mapList2 = authors.stream()
        .collect(Collectors.groupingBy(
            authorObj -> authorObj.getTransId() + "_" + authorObj.getCountryCode(),
            Collectors.reducing(
                new Author("", "", 0D, 0D),
                (x, y) -> { 
                    y.setCurrencyUSD(Double.sum(y.getCurrencyUSD(), x.getCurrencyUSD()));
                    y.setExccessAmount(Double.sum(y.getExccessAmount(), x.getExccessAmount())); 
                    return y; 
                })
            ));
        
}

抛出的异常信息

Exception in thread "main" java.lang.NullPointerException at Examples.App.lambda$1(App.java:206) at
java.base/java.util.stream.Collectors.lambda$reducing$42(Collectors.java:877) at
java.base/java.util.stream.Collectors.lambda$groupingBy$53(Collectors.java:1136) at
java.base/java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169) at
java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655) at
java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484) 

请问如何为代码添加空值检查来解决该问题?


解决方案

异常原因分析

NullPointerException大概率来自三个场景:

  1. 列表中存在null的Author对象
  2. Author对象的transId或countryCode为null,拼接字符串时触发空指针
  3. 调用getCurrencyUSD()或getExccessAmount()时,字段值为null

另外原有reducing逻辑存在隐患:直接修改流中的原对象会破坏原始数据,且累加顺序易导致计算错误。

带空值检查的修复代码

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

public class App {
    public static void prepareList() {

        List<Author> authors = // 初始化列表

        Map<String, Author> mapList2 = authors.stream()
                // 过滤列表中的null对象
                .filter(Objects::nonNull)
                // 过滤transId或countryCode为null的无效对象,避免拼接键时空指针
                .filter(author -> author.getTransId() != null && author.getCountryCode() != null)
                .collect(Collectors.groupingBy(
                        authorObj -> authorObj.getTransId() + "_" + authorObj.getCountryCode(),
                        Collectors.reducing(
                                new Author("", "", 0D, 0D),
                                (acc, current) -> {
                                    // 处理数值字段的空值,为null时用0D替代
                                    double totalCurrency = Double.sum(
                                            acc.getCurrencyUSD() != null ? acc.getCurrencyUSD() : 0D,
                                            current.getCurrencyUSD() != null ? current.getCurrencyUSD() : 0D
                                    );
                                    double totalExccess = Double.sum(
                                            acc.getExccessAmount() != null ? acc.getExccessAmount() : 0D,
                                            current.getExccessAmount() != null ? current.getExccessAmount() : 0D
                                    );
                                    // 返回新的聚合对象,保留正确的transId和countryCode
                                    return new Author(current.getTransId(), current.getCountryCode(), totalCurrency, totalExccess);
                                })
                ));
    }
}

关键空值检查点说明

  • 过滤空对象:用filter(Objects::nonNull)直接移除流中null的Author实例
  • 过滤无效键字段:确保transId和countryCode不为null,避免拼接字符串时触发NPE
  • 数值字段空值处理:累加前检查currencyUSD和exccessAmount是否为null,为null时用0D替代,避免Double.sum()触发空指针
  • 避免修改原对象:每次累加都创建新的Author对象,既保证原始数据不受影响,也避免因对象引用导致的意外问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 06:39:35