gpt4 book ai didi

java - 对于Java流,generate + limit是否保证不会额外调用生成器函数,或者是否有更好的替代方案?

转载 作者:行者123 更新时间:2023-12-02 02:38:51 24 4
gpt4 key购买 nike

我有一个数据源,我知道该数据源包含 n 个元素,我可以通过重复调用对象上的方法来访问该数据源;为了举例,我们将其称为 myReader.find()。我想创建一个包含这些 n 元素的数据流。还可以说,我不想调用 find() 方法的次数超过我想要返回的数据量,因为它会抛出异常(例如 NoSuchElementException >) 如果在到达数据末尾后调用该方法。

我知道我可以使用 IntStream.range 创建此流方法,并使用 find 方法映射每个元素。然而,这感觉有点奇怪,因为我完全忽略了流中的 int 值(我实际上只是使用它来生成恰好包含 n 元素的流)。

return IntStream.range(0, n).mapToObj(i -> myReader.read());

我考虑过的一种方法是使用 Stream.generate(supplier)接下来是 Stream.limit(maxSize) 。根据我对 limit 函数的理解,这感觉应该可行。

Stream.generate(myReader::read).limit(n)

但是,我在 API 文档中没有看到任何指示 Stream.limit() 方法将保证精确地由调用它的流生成 maxSize 元素。只要最终结果只是前 n 次调用,流实现就可以调用生成器函数超过 n 次,这并非不可行。只要它满足作为短路中间操作的 API 约定。

Stream.limit JavaDocs

Returns a stream consisting of the elements of this stream, truncated to be no longer than maxSize in length.This is a short-circuiting stateful intermediate operation.

Stream operations and pipelines documentation

An intermediate operation is short-circuiting if, when presented with infinite input, it may produce a finite stream as a result. [...] Having a short-circuiting operation in the pipeline is a necessary, but not sufficient, condition for the processing of an infinite stream to terminate normally in finite time.

仅依赖 Stream.generate(generator).limit(n) 对底层生成器进行 n 次调用是否安全?如果是这样,我是否缺少一些关于这一事实的文档?

并避免 XY Problem :通过精确执行操作n次来创建流的惯用方法是什么?

最佳答案

Stream.generate creates an unordered Stream 。这意味着后续的 limit 操作不需要使用前 n 个元素,因为当没有顺序时没有“第一个”,但可以选择任意 n 个元素。实现可能会利用此权限,例如以获得更高的并行处理性能。

以下代码

IntSummaryStatistics s =
Stream.generate(new AtomicInteger()::incrementAndGet)
.parallel()
.limit(100_000)
.collect(Collectors.summarizingInt(Integer::intValue));

System.out.println(s);

打印类似的内容

IntSummaryStatistics{count=100000, sum=5000070273, min=1, average=50000,702730, max=100207}

在我的机器上,而最大数量可能会有所不同。它表明 Stream 已根据需要准确选择了 100000 元素,但没有选择 1 到 100000 之间的元素。由于生成器生成严格升序的数字,因此很明显它已被调用超过 100000 次得到比这个更高的数字。

另一个例子

System.out.println(
Stream.generate(new AtomicInteger()::incrementAndGet)
.parallel()
.map(String::valueOf)
.limit(10)
.collect(Collectors.toList())
);

在我的机器上打印类似的内容(JDK-14)

[4, 8, 5, 6, 10, 3, 7, 1, 9, 11]

使用 JDK-8,它甚至会打印类似的内容

[4, 14, 18, 24, 30, 37, 42, 52, 59, 66]

如果像这样的结构

IntStream.range(0, n).mapToObj(i -> myReader.read())

由于未使用i参数,感觉很奇怪,您可以使用

Collections.nCopies(n, myReader).stream().map(TypeOfMyReader::read)

相反。这不会显示未使用的 int 参数,并且效果同样好,因为事实上,它在内部实现为 IntStream.range(0, n).mapToObj(i -> element).没有办法绕过某些计数器(无论是可见的还是隐藏的)来确保该方法将被调用n次。请注意,由于 read 可能是有状态操作,因此在启用并行处理时,生成的行为将始终像无序流一样,但 IntStreamnCopies code> 方法创建一个有限流,该流永远不会调用该方法超过指定次数。

关于java - 对于Java流,generate + limit是否保证不会额外调用生成器函数,或者是否有更好的替代方案?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63929040/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com