如果使用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[]
数组,只需将值处理为long
s。解决方案非常简单:
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));
请注意,这也适用于IntStream
s,因为从int
到long
的提升会在调用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数组[复制]