栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

为什么stream.spliterator()的tryAdvance会将项目累积到缓冲区中?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

为什么stream.spliterator()的tryAdvance会将项目累积到缓冲区中?

我大都同意@Holger的出色回答,但我会不同地强调口音。我认为您很难理解对缓冲区的需求,因为您对Stream
API所允许的思维模型非常简单。如果一个人想着流中的序列

map
filter
,也没有必要额外的缓冲,因为这些行动有2个重要的“好”的属性:

  1. 一次处理一个元素
  2. 结果产生0或1个元素

但是,在一般情况下,这些都不是正确的。正如@Holger所提到的

flatMap
,Java
8中已经打破了规则2,在Java9中,他们终于添加了takeWhile,它实际上是在整个
Stream
->
Stream
而不是在每个元素的基础上进行转换的(并且这是AFAIK的第一个中间衬衫循环操作)。

我不太同意@Holger的另一点是,我认为最根本的原因与他在第二段中提到的原因(即a)有些不同,您可能会称其

tryAdvance
Stream
多次结束,而b)“ 没有保证呼叫者将始终通过相同的消费者 ”)。我认为最重要的原因是,
Spliterator

在功能上必须相同,
Stream
必须支持短路和惰性(即不处理全部内容
Stream
或不支持未绑定流的能力)。换句话说,即使Spliterator
API(相当奇怪)要求您必须使用同一
Consumer
对象的所有方法的所有调用一个给定的
Spliterator
,你仍然需要
tryAdvance
tryAdvance
实现仍然必须使用一些缓冲区。如果您所拥有的只是全部,就无法停止处理数据,
forEachRemaining(Consumer<?super T>)
因此您将无法实现与之相似
findFirst
takeWhile
使用的任何数据。实际上,这是JDK实现内部使用
Sink
接口而不是接口
Consumer
(以及“换行”
wrapAndCopyInto
代表的意思)的原因之一:
Sink
具有其他
boolean cancellationRequested()
方法。

所以 总结一下 :需要一个缓冲,因为我们希望

Spliterator

  1. 使用简单方法
    Consumer
    ,它不提供报告处理/取消后端的方法
  2. 提供一种手段,可以根据(逻辑)使用者的请求停止处理数据。

请注意,这两个要求实际上有点矛盾。

示例和一些代码

在这里,我想提供一些代码示例,我认为如果没有当前的API协定(接口),没有附加缓冲区就无法实现。本示例基于您的示例。

有一个简单的整数整数Collat​​z序列被猜想总是最终命中1。AFAIK该猜想尚未得到证明,但已针对许多整数(至少对于整个32位int范围)进行了验证。

因此,假设我们要解决的问题如下:从Collat​​z序列流中的1到1,000,000范围内的随机起始数字中查找第一个以十进制表示形式包含“ 123”的数字。

这是一个仅使用

Stream
(不是
Spliterator
)的解决方案:

static String findGoodNumber() {    return new Random() .ints(1, 1_000_000) // unbound! .flatMap(nr -> collatzSequence(nr)) .mapToObj(Integer::toString) .filter(s -> s.contains("123")) .findFirst().get();}

这里

collatzSequence
是一个返回
Stream
包含Collat​​z序列直到第一个1
的函数(对于nitpickers,请让它在当前值大于时也停止,
Integer.MAX_VALUE /3
这样我们就不会溢出)。

每一个这样

Stream
的返回
collatzSequence
势必。标准
Random
也会最终生成所提供范围内的每个数字。这意味着我们可以保证流中最终会有一些“好”的数字(例如
123
),并且
findFirst
存在短路,因此整个操作实际上将终止。但是,没有合理的Stream
API实现可以预测这一点。

现在假设由于某些奇怪的原因,您想使用middle来执行相同的操作

Spliterator
。即使您只有一个逻辑并且不需要不同
Consumer
的,也不能使用
forEachRemaining
。因此,您必须执行以下操作:

static Spliterator<String> createCollatzRandomSpliterator() {    return new Random() .ints(1, 1_000_000) // unbound! .flatMap(nr -> collatzSequence(nr)) .mapToObj(Integer::toString) .spliterator();}static String findGoodNumberWithSpliterator() {    Spliterator<String> source = createCollatzRandomSpliterator();    String[] res = new String[1]; // work around for "final" closure restriction    while (source.tryAdvance(s -> {        if (s.contains("123")) { res[0] = s;        }    })) {        if (res[0] != null) return res[0];    }    throw new IllegalStateException("Impossible");}

同样重要的是,对于某些起始数字,Collat​​z序列将包含多个匹配数字。例如,

41123
123370
(== 41123 * 3 + 1)都包含“
123”。这意味着我们真的不希望
Consumer
在第一个匹配命中后被调用。但是由于
Consumer
没有公开报告处理结束的任何方法,
WrappingSpliterator
所以不能仅仅将我们传递
Consumer
给内部
Spliterator
。唯一的解决方案是将内部的所有结果
flatMap
(以及所有后处理)累积到某个缓冲区中,然后一次在该缓冲区上迭代一个元素。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/610027.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号