如果使用Arrays Stream的整数数组的总和超过int max value(2147483647),如何处理?

Posted

技术标签:

【中文标题】如果使用Arrays Stream的整数数组的总和超过int max value(2147483647),如何处理?【英文标题】:How to handle if sum of integer array exceeds int max value(2147483647) using Arrays Stream? 【发布时间】:2017-04-08 06:39:24 【问题描述】:

我有一个整数数组,想对它的所有元素求和。只要 sum 小于或等于 int 最大值(2147483647),它就可以正常工作,但是一旦 sum 超过 int 最大值,它就会失败。以下是示例-:

int [] iArr = new int[]2147483646 , 1 , 1;
    System.out.println(Arrays.stream(iArr).sum());

结果-:-2147483648

我们可以看到上面的结果是不正确的。我知道它可以通过使用 long 数组而不是 int 数组来解决,如下所示。

long [] iArr = new long[]2147483646 , 1 , 1;
    System.out.println(Arrays.stream(iArr).sum());

结果-:2147483648

但我不想使用长数组,因为我知道数组元素的最大大小限制是 int 最大值(2147483647)。因此我不想浪费内存。有没有其他方法可以使用 Array Stream 对整数数组求和(数组元素的总和)超过 int 最大值(2147483647)?

【问题讨论】:

【参考方案1】:

您不需要long[] 数组,只需将值处理为longs。解决方案非常简单:

int[] iArr = new int[]2147483646 , 1 , 1;
System.out.println(Arrays.stream(iArr).asLongStream().sum());

不可能超过long 范围,因为即使所有元素都与Integer.MAX_VALUE 一样大,您也需要4294967298 个流元素才能到达Long.MAX_VALUE。但是如果你有这么大的流量和大量的时间,或者当你的起点是LongStream,你可以使用BigInteger,如下所示:

long[] lArr= Long.MAX_VALUE, Long.MAX_VALUE, 42 ;
System.out.println(Arrays.stream(lArr).mapToObj(BigInteger::valueOf)
                         .reduce(BigInteger.ZERO, BigInteger::add));

请注意,这也适用于IntStreams,因为从intlong 的提升会在调用BigInteger.valueOf(long) 时自动发生,但如上所述,很少需要这样做。

【讨论】:

【参考方案2】:

这个问题已经得到解答,但我找到了另一种以最佳方式完成上述任务的方法。这就是为什么我想与执行时间统计数据分享这一点。 asLongStream 流方法最适合将整数流转换为长流。

int[] iArr = new int[10000000];
Arrays.fill(iArr , 3132);   

long iStart = System.nanoTime();
long  lSum1 = Arrays.stream(iArr).mapToLong(Long::valueOf).sum();
long iEnd = System.nanoTime();

System.out.println("Sum-: " + lSum1);
System.out.println("Time in nanoseconds-: " + (iEnd - iStart) + "ns using \"mapToLong\"\n");

long iStart1 = System.nanoTime();
long lSum2 = Arrays.stream(iArr).asLongStream().sum();
long iEnd1 = System.nanoTime();

System.out.println("Sum-: " + lSum2);
System.out.println("Time in nanoseconds-: " + (iEnd1 - iStart1) + "ns using \"asLongStream\"\n");

long iStart2 = System.nanoTime();
long lSum3 = Arrays.stream(iArr).boxed().collect(Collectors.summingLong(Integer::longValue));
long iEnd2 = System.nanoTime();

System.out.println("Sum-: " + lSum3);
System.out.println("Time in nanoseconds-: " + (iEnd2 - iStart2) + "ns using \"Collectors\"\n");

输出-:

总和-:31320000000 以纳秒为单位的时间-:230369419ns 使用“mapToLong”

总和-:31320000000 以纳秒为单位的时间-: 109999469ns 使用“asLongStream”

总和-:31320000000 以纳秒为单位的时间-: 188769476ns 使用“收集器”

【讨论】:

由于规范的解决方案.asLongStream() 到目前为止还没有被提及,很遗憾你把它隐藏在基准测试的某个地方。我只是在发布我的答案后才看到它。无论如何,它值得我 +1... 请注意,您正在衡量打印结果的成本,这不应被低估。并且您应该在循环中多次运行代码以区分初始化成本和实际计算。此外,System.nanoTime() 是测量经过时间的首选方式,因为它独立于“挂钟”时间,因此不受 NTP 更新、闰秒等影响。 @Holger ,我更新了 System.nanoTime() 而不是 System.currentTimeMillis() 来测量经过的时间。【参考方案3】:

如果 long 大到足以容纳结果,那么您可以使用收集器将值与 long 相加:

Arrays.stream(is).boxed()
      .collect(Collectors.summingLong(Integer::longValue));

如果您需要比 long 更大,您可能需要使用 BigInteger,如下所示:

Arrays.stream(is).boxed()
      .map(Integer::longValue)
      .collect(Collectors
      .reducing(new BigInteger("0"), BigInteger::valueOf, (b1, b2) -> b1.add(b2)));

【讨论】:

.boxed().map(Integer::longValue) 有一个不必要的中间框 Integer 步骤。你可以使用.mapToObj(Long::valueOf)。您也可以使用常量BigInteger.ZERO 代替new BigInteger("0"),并且(b1, b2) -> b1.add(b2) 可以替换为BigInteger::add。但即便如此,这仍然是still way too complicated。求和为long 根本不需要装箱。【参考方案4】:

问题是 sum 的返回类型与流的类型匹配。如果先将其映射到 Longs 流,则可以正确返回总和。

int [] iArr = new int[]2147483646 , 1 , 1;
Long sum = Arrays.stream(iArr)
        .mapToLong(Long::valueOf)
        .sum();
System.out.println(sum);

【讨论】:

.mapToLong(…) 在您想根据int 值计算long 时很方便,但对于简单的类型提升,您可以只使用IntStream.asLongStream()。此外,之后没有理由将 long 值装箱到 Long 对象中。 太棒了!是的,那好多了:)

以上是关于如果使用Arrays Stream的整数数组的总和超过int max value(2147483647),如何处理?的主要内容,如果未能解决你的问题,请参考以下文章

使用Arrays.asList方法使用Stream打印int数组[复制]

将数组转换为Stream流的两种方式--- Stream.of(数组名) 与 Arrays.stream(数组名)

javaStream的使用

获取数组内的数组总和

Java Stream按条件减少多个列表

Stream