简体   繁体   English

使用 Stream.sum() 多次使用 stream

[英]Consuming stream multiple times with Stream.sum()

A stream should be operated on (invoking an intermediate or terminal stream operation) only once. stream 应该只运行一次(调用中间或终端 stream 操作)。

I get the idea, but how come finding the sum of a stream does not consume it?我明白了,但是为什么找到 stream 的总和不消耗它? I can run the code below, without any exceptions.我可以运行下面的代码,没有任何例外。

double totalPrice = stream.mapToDouble(product -> product.price).sum();
List<Product> products = stream.map(this::convert).collect(Collectors.toList());

Why sum is not a terminal operator?为什么sum不是终端运算符? How is it different from collecting stream elements into a list?与将 stream 元素收集到列表中有何不同?

Documentation文档

Whether a stream can be consumed once or multiple times is not specified.未指定 stream 是否可以使用一次或多次。 The documentation says " should be operated... only once ", not " must " or " can ".文档说“应该操作......只操作一次”,而不是“必须”或“可以”。

It depends on the underlying implementation and source of the stream.取决于stream 的底层实现和来源。 As such, the capability to execute it multiple times is undefined for streams in general.因此,对于流来说,多次执行它的能力通常是未定义的。


Implementations实现

You might be able to find a stream that can be consumed twice or multiple times, especially for custom implementations or implementations where a prevention detection would maybe be too expensive to implement.您可能能够找到可以使用两次或多次的 stream,特别是对于自定义实现或预防检测可能过于昂贵而无法实现的实现。

But again, it is not supported by the documentation.但同样,文档不支持它。 It may be by a current implementation, but this can change at any time, in any Java release.它可能是当前的实现,但这可以随时更改,在任何 Java 版本中。 It may throw exceptions, it may result in buggy behavior, it is not specified.它可能会抛出异常,可能会导致错误行为,但未指定。

Here is a small example which uses sum() that throws an exception because it has such a detection (JDK 11):这是一个小示例,它使用sum()引发异常,因为它具有这样的检测(JDK 11):

Stream<Integer> stream = List.of(1, 2, 3, 4).stream();

int first = stream
    .mapToInt(i -> i)
    .sum();

int second = stream
    .mapToInt(i -> i)
    .sum();

// throws IllegalStateException: stream has already been operated upon or closed

Conclusion结论

The intention is definitely to not use a stream multiple times , as hinted by the documentation.正如文档所暗示的,其意图绝对是不要多次使用 stream So even if it is maybe possible for a certain implementation, avoid it .因此,即使某个实现可能是可行的,也要避免它


Why?为什么?

You may ask yourself why Stream does not support multiple iteration.你可能会问自己为什么Stream不支持多次迭代。 After all, collections like ArrayList have no issue with that and it seems to be a common use case.毕竟,像 ArrayList 这样的ArrayList没有任何问题,而且这似乎是一个常见的用例。

Streams have a much bigger scope than collections.流的scope比 collections 大得多。 A stream created on a typical collection could likely easily support multiple iterations.在典型集合上创建的 stream 可能很容易支持多次迭代。 But a stream tied to a resource like a file or web-connection, for example the stream returned by Files.lines(...) could not.但是 stream 与文件或网络连接等资源相关联,例如Files.lines(...)返回的 stream 不能。 Such a feature would be extremely resource heavy and expensive for files and maybe not even supported for others, like a web-connection which may be closed afterwards.这样的功能对于文件来说将是非常耗费资源和昂贵的,甚至可能不支持其他功能,例如之后可能会关闭的 Web 连接。

It goes even further, you can easily create an infinite stream that generates random numbers:更进一步,您可以轻松创建一个无限的 stream 生成随机数:

Stream<Double> stream = Stream.generate(Math::random);

While it could easily support multiple usages, it would be impossible to generate the same sequence again when iterated again.虽然它可以很容易地支持多种用途,但再次迭代时不可能再次生成相同的序列。

Another example, a stream that consumes resources without restoring them:另一个例子,一个 stream 消耗资源而不恢复它们:

Queue<Integer> queue = ...
Stream<Integer> stream = Stream.generate(queue::poll);

This stream will remove elements from the queue.这个 stream 将从队列中删除元素。 They are gone after stream usage.他们在 stream 使用后消失了。 Another iteration of the stream wont be able to get hands on the dead objects anymore. stream 的另一个迭代将无法再接触死物。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM