Spliterator
从Stream
管道获取可能会返回StreamSpliterators.WrappingSpliterator的实例。例如,获取以下内容Spliterator
:
Spliterator<String> source = new Random()
.ints(11, 0, 7) // size, origin, bound
.filter(nr -> nr % 2 != 0)
.mapToObj(Integer::toString)
.spliterator();
鉴于以上所述Spliterator<String> source
,当我们通过的tryAdvance (Consumer<? super P_OUT> consumer)
方法(Spliterator
在本例中为StreamSpliterators.WrappingSpliterator的一个实例)单独遍历元素时,它将首先将项目累积到内部缓冲区中,然后再使用这些项目,如我们在StreamSpliterators.java中所看到的那样。
#298
。从简单的角度来看,doAdvance()
先将项目插入buffer
,然后再获取下一个项目并将其传递给consumer.accept (…)
。
public boolean tryAdvance(Consumer<? super P_OUT> consumer) {
boolean hasNext = doAdvance();
if (hasNext)
consumer.accept(buffer.get(nextToConsume));
return hasNext;
}
但是,我没有弄清楚这一点的必要性buffer
。
在这种情况下,为什么不将的consumer
参数tryAdvance
简单地用作Sink
管道的终端?
我大都同意@Holger的出色回答,但我会不同地强调口音。我认为您很难理解对缓冲区的需求,因为您对Stream
API所允许的思维模型非常简单。如果一个人想着流中的序列map
和filter
,也没有必要额外的缓冲,因为这些行动有2个重要的“好”的属性:
但是,在一般情况下,这些都不是正确的。正如@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
:
Consumer
,它不提供报告处理/取消后端的方法请注意,这两个要求实际上有点矛盾。
示例和一些代码
在这里,我想提供一些代码示例,我认为如果没有当前的API协定(接口),没有附加缓冲区就无法实现。本示例基于您的示例。
有一个简单的整数整数Collatz序列被猜想总是最终命中1。AFAIK该猜想尚未得到证明,但已针对许多整数(至少对于整个32位int范围)进行了验证。
因此,假设我们要解决的问题如下:从Collatz序列流中的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
包含Collatz序列直到第一个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");
}
同样重要的是,对于某些起始数字,Collatz序列将包含多个匹配数字。例如,41123
和123370
(== 41123 * 3 + 1)都包含“
123”。这意味着我们真的不希望Consumer
在第一个匹配命中后被调用。但是由于Consumer
没有公开报告处理结束的任何方法,WrappingSpliterator
所以不能仅仅将我们传递Consumer
给内部Spliterator
。唯一的解决方案是将内部的所有结果flatMap
(以及所有后处理)累积到某个缓冲区中,然后一次在该缓冲区上迭代一个元素。
在zlib的deflate实现中,注意到当给定的数据长度较小时,zlib函数会将数据复制到输入缓冲区,只有当输入缓冲区满时才开始压缩。 当给定的数据长度大于输入缓冲区大小时,zlib函数直接开始压缩。 我想知道将短数据积累到输入缓冲区中有什么好处。我能想到的是: 避免花费在块处理上的开销,包括Huffman树初始化和CRC caculation 保持I/O工作在8KB(或更大)块上,有利于提高性能
我们正在迁移到Spring WebFlux(带有Reactornetty)。应用程序使用HTTP协议和Spring控制器。目前,我们有一种过渡解决方案,它将入站IO缓冲区累积到合成字节buf中,而不进行复制(然后将其作为输入流进行处理)。Reactornetty为我们提供直接字节缓冲区。因此,为这些缓冲区调用是至关重要的。最初我们有代码: 在处理得到的复合缓冲液后进行释放。 但当上游发布器发出错误
我读到FileWriter和BufferedWriter的区别在于FileWriter直接写入文件(逐字符),white BufferedReader使用缓冲区。如果是,为什么FileWriter有缓冲区?例如,如果我创建一个FileWriter对象,如下所示: 而且,如果我在程序结束时不刷新或关闭写入器,它将不会向文件写入任何内容。这意味着它也使用缓冲区。拜托,解释一下?
两者都是序列化库,由谷歌开发人员开发。他们之间有什么大的区别吗?将使用协议缓冲区的代码转换为使用FlatBuffers需要大量工作吗?
问题内容: python中有一个类型,但是我不知道该如何使用它。 在Python文档中,描述为: object参数必须是支持缓冲区调用接口的对象(例如字符串,数组和缓冲区)。将创建一个引用该对象参数的新缓冲区对象。缓冲区对象将从对象的开头(或指定的偏移量)开始是一个切片。切片将延伸到对象的末尾(或具有由size参数指定的长度)。 问题答案: 用法示例: 在这种情况下,缓冲区是一个子字符串,从位置6
我有一个JSON对象,我正在将它转换成一个,并在这里进行一些处理。稍后,我想将相同的缓冲区数据转换为有效的JSON对象。 我的工作节点V6.9.1 下面是我尝试过的代码,但当我转换回JSON并且无法打开此对象时,我得到了。 所以我试着用检查的方式打印整个物体 如果我试着像数组一样读取它 我试图解析它也抛出SynTaxError:意外令牌o在JSON在位置2 我需要像我创建的那样将其视为真实对象(我