Java 流在流式传输数组时会分配大量空间吗?

问题描述 投票:0回答:1

假设我们有一段代码,例如:

Arrays.stream(queries)
  .limit(queries.length - 1).mapToInt(i -> i)
  .sum();

其中querys是一个由N个整数组成的数组。 为了澄清这个问题,假设它有 100 万个整数,因此该数组将占用 ~4MB(每个整数 1M * 4 字节)。

流会占用相当大的空间吗?或者我们是否会使用大约 4MB 并流式传输数组,而无需重新分配整个数组来运行以下代码(不考虑运行 JVM 所需的空间)?

java java-stream
1个回答
4
投票

答案是:

[A] 实施细节。 java 规范根本不会告诉你,因此任何确切的答案都需要用“.. 在这个硬件、这个操作系统、这个 VM impl、这个版本,在这些情况下”来说明。然而...

[B] 无论答案是什么,它都是“相当快/空间不大”,而且绝对“不依赖于 N 的值”。

stream 中的“Stream”并不是为了好玩而选择的:事实上,stream API 确实是stream。它不会获取整个数组,然后创建一个包含准备好流式传输的所有值的新对象,然后

limit
制作另一个新的巨型数组(尺寸小一倍),然后
mapToInt
制作另一个。这不是的工作原理。

Stream 是一个管道。在运行终端命令(

sum

 是终端)之前,不会发生任何事情。你可以检查一下:

Arrays.stream(queries).mapToInt(i -> { System.out.println(i); return i.intValue(); });
这不会打印任何内容。完全没有。因为这只是一个半生不熟的流过程,没有终端,所以它不会“流动”。

如果您在上面调用 sum,那么打印就会开始发生。具体来说,终端(此处为

sum()

)开始“从流中提取值”。这样就往上走。 sum 向 
mapToInt
 请求一个值,为此,
mapToInt
limit
 请求一个值(然后获取该值,将其滚动通过 
i -> i
 lambda,并将其提供给 
sum
)。然后 
limit
 会向 
Arrays.stream
 询问一个值,然后实际从数组中读取单个项目。涉及 
ARE 中间跟踪器对象,但它们的大小不依赖于 N。例如,Arrays.stream(queries)
 返回的对象保存对 
queries
 数组的引用(大约 64 位数据,无论该数组有多大)是;只是一个指针),以及一个知道我们在哪里的 int 值 
1

代表其中

limit

 部分的对象只有一个 
int
 ,用于跟踪到目前为止提供了多少个值。 
limit
 当它所拉出的东西用完时,就像没有更多的值可以提供 
limit
 项目已经提供,以较早发生者为准。

等等。因此,这些跟踪器对象到底有多大是一个实现细节,但是它们是“小”(至少相对于一百万个整数数组而言!),并且不依赖于流的大小。事实上,无限流可以存在,没有问题。他们确实这样做了 - 检查

Stream

 本身的 API,例如,您可以在其中轻松创建一个返回无限量 
1
 值的流。

[1] 我过于简单化了。流还具有根据某些情况可以并行化的特性。当涉及计数器时,并行化变得非常困难,因此这些跟踪器有点复杂。如果您想要完整的详细信息,请查看

SpliteratorStreamUtils

。但是,这种过于简单化的解释足以理解,任何流中间操作都不会让您面临内存不足的风险。

© www.soinside.com 2019 - 2024. All rights reserved.