Java中使用Stream对Integer集合求和避免溢出的方法
这个问题我之前也碰到过!当你用mapToInt求和时,哪怕单个元素都远小于Integer.MAX_VALUE,只要元素数量足够多,总和很容易就超出int的范围,触发静默溢出,结果完全不对。下面给你两种靠谱的解决方案:
解决方案一:转成long类型求和
这是最常用的方案,因为long的取值范围(-9223372036854775808 到 9223372036854775807)比int大得多,绝大多数业务场景下都能避免溢出。只需要把mapToInt换成mapToLong就行:
Set<Integer> numbers = new HashSet<>(); // 填充集合的代码 long sum = numbers.stream().mapToLong(Integer::longValue).sum();
这里Integer::longValue会把每个Integer对象转成long基本类型,最后sum()返回的是long类型,完美避开int的溢出问题。
解决方案二:用BigInteger实现无溢出求和
如果你的元素数量多到连long都兜不住(比如几十亿个接近Integer.MAX_VALUE的元素),那就要用BigInteger来求和,它支持任意大小的整数运算,完全不会溢出。代码如下:
Set<Integer> numbers = new HashSet<>(); // 填充集合的代码 BigInteger total = numbers.stream() .map(BigInteger::valueOf) .reduce(BigInteger.ZERO, BigInteger::add);
简单解释下逻辑:
map(BigInteger::valueOf)把每个Integer转成BigInteger对象reduce方法以BigInteger.ZERO为初始值,通过BigInteger::add把所有元素累加起来
这种方式绝对安全,但因为是对象操作,性能会比基本类型的流稍差一点,不过在需要绝对避免溢出的场景下,这点性能损耗完全值得。
补充:为什么原来的代码会溢出?
Java的整数溢出是静默的——当总和超过Integer.MAX_VALUE时,不会抛出异常,而是直接按照二进制补码规则循环计算,结果会变成一个错误的负数或者小正数,很难排查。比如把多个Integer.MAX_VALUE相加,得到的结果会完全不符合预期。
你可以根据自己的业务场景选择对应的方案哦!
内容的提问来源于stack exchange,提问作者bisarch




