简体   繁体   English

深刻理解分裂者的特征

[英]Understanding deeply spliterator characteristics

In order to try to deeply understand java streams and spliterators, I have some subtle questions about spliterator characteristics : 为了尝试深入理解java流和分裂器,我对spliterator特性有一些微妙的问题:

Q1: Stream.empty() vs Stream.of() (Stream.of() without args) Q1: Stream.empty() vs Stream.of() (没有args的Stream.of())

  • Stream.empty() : SUBSIZED, SIZED Stream.empty()SUBSIZED,SIZED
  • Stream.of() : SUBSIZED, IMMUTABLE , SIZED, ORDERED Stream.of()SUBSIZED, IMMUTABLE ,SIZED, ORDERED

Why Stream.empty() doesn't have the same characteristics of Stream.of() ? 为什么Stream.empty()不具有相同的特点Stream.of() Note that it has impacts when using in conjunction with Stream.concat() (specially not having ORDERED ). 请注意,与Stream.concat()(特别是没有ORDERED )一起使用时会产生影响。 I would say that Stream.empty() should have not just IMMUTABLE and ORDERED but also DISTINCT and NONNULL . 我会说Stream.empty()不应该只有IMMUTABLE和ORDERED ,还要有DISTINCT和NONNULL Also it make sense Stream.of() with only one argument having DISTICT . 同样有意义的Stream.of()只有一个参数有DISTICT

Q2: LongStream.of() not having NONNULL Q2: LongStream.of()没有NONNULL

Just noticed that NONNULL is not available in LongStream.of . 刚刚注意到NONNULL在LongStream.of不可用。 Isn't NONNULL a main characteristics of all LongStream s, IntStream s and DoubleStream s? NONNULL不是所有LongStreamIntStreamDoubleStream的主要特征吗?

Q3: LongStream.range(,) vs LongStream.range(,).boxed() Q3: LongStream.range(,) vs LongStream.range(,).boxed()

  • LongRange.range(,) : SUBSIZED, IMMUTABLE, NONNULL , SIZED, ORDERED, SORTED, DISTINCT LongRange.range(,)SUBSIZED, IMMUTABLE,NONNULL ,SIZED,ORDERED, SORTED ,DISTINCT
  • LongStream.range(,).boxed() : SUBSIZED, SIZED, ORDERED LongStream.range(,).boxed()SUBSIZED,SIZED,ORDERED

Why .boxed() loses all these characteristics? 为什么.boxed()失去了所有这些特征? It shouldn't lose any. 它不应该失去任何。

I understand that .mapToObj() can lose the NONNULL, IMMUTABLE and DISTICT , but .boxed() ... doesn't make sense. 我知道.mapToObj()会失去NONNULL,IMMUTABLE和DISTICT ,但是.boxed() ......没有意义。

Q4: .peek() loses IMMUTABLE and NONNULL Q4: .peek()失去IMMUTABLE和NONNULL

LongStream.of(1) : SUBSIZED, IMMUTABLE, NONNULL, SIZED, ... LongStream.of(1).peek() : SUBSIZED, SIZED, ... LongStream.of(1)SUBSIZED,IMMUTABLE,NONNULL,SIZED,... LongStream.of(1).peek()SUBSIZED,SIZED,...

Why .peek() loses these characteristics? 为什么.peek()失去了这些特征? .peek shouldn't really lose any. .peek不应该真的失去任何。

Q5: .skip() , .limit() loses SUBSIZED, IMMUTABLE, NONNULL, SIZED Q5: .skip() .limit()丢失了SUBSIZED,IMMUTABLE,NONNULL,SIZED

Just notice that these operations loses SUBSIZED, IMMUTABLE, NONNULL, SIZED . 请注意,这些操作失去了SUBSIZED,IMMUTABLE,NONNULL,SIZED Why? 为什么? If the size is available, then it's easy to calculate the final size as well. 如果尺寸可用,那么也很容易计算最终尺寸。

Q6: .filter() loses IMMUTABLE, NONNULL Q6: .filter()失去IMMUTABLE,NONNULL

Just notice that this operations loses as well SUBSIZED, IMMUTABLE, NONNULL, SIZED . 请注意,此操作失败了SUBSIZED,IMMUTABLE,NONNULL,SIZED It make sense to lose SUBSIZED and SIZED , but the others two doesn't make sense. 丢失SUBSIZED和SIZED是有道理的,但其他两个没有意义。 Why? 为什么?


I will appreciate if someone that understands deeply the spliterator could bring some clarity. 如果能够深入理解分裂者的人能够带来一些清晰度,我将不胜感激。 Thanks. 谢谢。

I have to admit that I had difficulties too when I first tried to find out the actual meaning of the characteristics and had the feeling that their meaning was not clearly settled during the implementation phase of Java 8 and is used inconsistently for that reason. 我不得不承认,当我第一次尝试找出特征的实际含义时,我也遇到了困难,并且感觉它们的意义在Java 8的实现阶段没有明确解决,并且由于这个原因而使用不一致。

Consider Spliterator.IMMUTABLE : 考虑Spliterator.IMMUTABLE

Characteristic value signifying that the element source cannot be structurally modified; 表示元素源不能进行结构修改的特征值; that is, elements cannot be added, replaced, or removed, so such changes cannot occur during traversal. 也就是说,无法添加,替换或删除元素,因此在遍历期间不会发生此类更改。

It's strange to see “replaced” in this list, which is usually not considered a structural modification when speaking of a List or an array and consequently, stream and spliterator factories accepting an array (that is not cloned) report IMMUTABLE , like LongStream.of(…) or Arrays.spliterator(long[]) . 在这个列表中看到“替换”是很奇怪的,当谈到List或数组时通常不会将其视为结构修改因此,接受数组(未克隆)的流和spliterator工厂报告IMMUTABLE ,如LongStream.of(…)Arrays.spliterator(long[])

If we interpret this more generously as “as long as not observable by the client”, there is no significant difference to CONCURRENT , as in either case some elements will be reported to the client without any way to recognize whether they were added during traversal or whether some were unreported due to removal, as there is no way to rewind a spliterator and compare. 如果我们更慷慨地将其解释为“只要客户无法观察”,则对于CONCURRENT没有显着差异,因为在任何一种情况下, 某些元素将被报告给客户而无需任何方式来识别它们是否在遍历期间添加或是否因为删除而未报告某些内容,因为无法回放分裂器并进行比较。

The specification continues: 规范继续:

A Spliterator that does not report IMMUTABLE or CONCURRENT is expected to have a documented policy (for example throwing ConcurrentModificationException ) concerning structural interference detected during traversal. 不报告IMMUTABLECONCURRENT IMMUTABLE应该具有关于遍历期间检测到的结构干扰的文档化策略(例如抛出ConcurrentModificationException )。

And that's the only relevant thing, a spliterator reporting either, IMMUTABLE or CONCURRENT , is guaranteed to never throw a ConcurrentModificationException . 这是唯一相关的事情,报告, IMMUTABLECONCURRENT IMMUTABLE器保证永远不会抛出ConcurrentModificationException Of course, CONCURRENT precludes SIZED semantically, but that has no consequence to the client code. 当然, CONCURRENT语义上排除了SIZED ,但这对客户端代码没有任何影响。

In fact, these characteristics are not used for anything in the Stream API, hence, using them inconsistently would never get noticed somewhere. 实际上,这些特性并不用于Stream API中的任何内容,因此,使用它们不一致的地方永远不会被注意到。

This is also the explanation why every intermediate operation has the effect of clearing the CONCURRENT , IMMUTABLE and NONNULL characteristics: the Stream implementation doesn't use them and its internal classes representing the stream state do not maintain them. 这也解释了为什么每个中间操作都具有清除CONCURRENTIMMUTABLENONNULL特性的效果:Stream实现不使用它们,并且表示流状态的内部类不维护它们。


Likewise, NONNULL is not used anywhere, so it's absence for certain streams has no effect. 同样, NONNULL不会在任何地方使用,因此某些流没有效果。 I could track down the LongStream.of(…) issue down to the internal use of Arrays.spliterator(long[], int, int) which delegates to 我可以LongStream.of(…)问题,直到内部使用Arrays.spliterator(long[], int, int)委托给
Spliterators.spliterator​(long[] array, int fromIndex, int toIndex, int additionalCharacteristics) : Spliterators.spliterator​(long[] array, int fromIndex, int toIndex, int additionalCharacteristics)

The returned spliterator always reports the characteristics SIZED and SUBSIZED . 返回的spliterator始终报告SIZEDSUBSIZED的特征。 The caller may provide additional characteristics for the spliterator to report. 呼叫者可以为分裂者报告提供额外的特征。 (For example, if it is known the array will not be further modified, specify IMMUTABLE ; if the array data is considered to have an encounter order, specify ORDERED ). (例如,如果已知数组将不会被进一步修改,请指定IMMUTABLE ;如果数组数据被视为具有遭遇顺序,请指定ORDERED )。 The method Arrays.spliterator(long[], int, int) can often be used instead, which returns a spliterator that reports SIZED , SUBSIZED , IMMUTABLE , and ORDERED . 通常可以使用方法Arrays.spliterator(long[], int, int) ,它返回一个报告SIZEDSUBSIZEDIMMUTABLEORDERED IMMUTABLE

Note (again) the inconsistent use of the IMMUTABLE characteristic. 注意(再次) IMMUTABLE特性的不一致使用。 It is again treated like having to guaranty the absence of any modification, while at the same time, Arrays.spliterator and in turn Arrays.stream and LongStream.of(…) will report the IMMUTABLE characteristic, even by specification, without being able to guaranty that the caller won't modify their array. 它再次被视为必须保证不存在任何修改,同时, Arrays.spliterator以及Arrays.streamLongStream.of(…)将报告IMMUTABLE特性,即使是通过规范,也无法保证调用者不会修改他们的数组。 Unless we consider setting an element not to be a structural modification, but then, the entire distinction becomes nonsensical again, as arrays can't be structurally modified. 除非我们考虑将元素设置为不是结构修改,否则,整个区别再次变得无意义,因为数组不能在结构上进行修改。

And it clearly specified no NONNULL characteristic. 它明确指出没有NONNULL特征。 While it is obvious that primitive values can't be null , and the Spliterator.Abstract<Primitive>Spliterator classes invariably inject a NONNULL characteristic, the spliterator returned by Spliterators.spliterator​(long[],int,int,int) does not inherit from Spliterator.AbstractLongSpliterator . 虽然原始值显然不能为null ,而Spliterator.Abstract<Primitive>Spliterator类总是会注入NONNULL特性,但Spliterators.spliterator​(long[],int,int,int)返回的Spliterators.spliterator​(long[],int,int,int)器不会继承自Spliterator.AbstractLongSpliterator

The bad thing is, this can't be fixed without changing the specification, the good thing is, it has no consequences anyway. 不好的是,这不能在不改变规格的情况下修复,好的是,它无论如何都没有后果。


So if we ignore any issues with CONCURRENT , IMMUTABLE , or NONNULL , which have no consequences, we have 因此,如果我们忽略了CONCURRENTIMMUTABLENONNULL任何问题,这些问题没有任何后果,我们有

SIZED and skip & limit . SIZEDskiplimit This is a well known issue, result of the way skip and limit have been implemented by the Stream API. 这是一个众所周知的问题,Stream API实现了skiplimit的方式。 Other implementations are imaginable. 其他实现是可以想象的。 This also applies to the combination of an infinite stream with a limit , which should have a predictable size, but given the current implementation, has not. 这也适用于无限流与limit的组合,其应具有可预测的大小,但是在当前实现的情况下,还没有。

Combining Stream.concat(…) with Stream.empty() . Stream.concat(…)Stream.empty()结合使用。 It sounds reasonable that an empty stream does not impose constraints on the result order. 这听起来是合理的空流结果顺序施加约束。 But Stream.concat(…) 's behavior of releasing the order when only one input has no order, is questionable. Stream.concat(…)在只有一个输入没有顺序时释放顺序的行为是值得怀疑的。 Note that being too aggressive regarding ordering is nothing new, see this Q&A regarding a behavior that was considered intentional first, but then has been fixed as late as Java 8, update 60. Perhaps, Stream.concat should have been discussed right at this point of time too… 请注意,对于排序来说过于激进并不是什么新鲜事,请参阅此问答,关于首先被认为是故意的行为,但后来已经修复到Java 8,更新60.也许, Stream.concat应该在此时讨论过时间也是......

The behavior of .boxed() is easy to explain. .boxed()的行为很容易解释。 When it has been implemented naively like .mapToObj(Long::valueOf) , it will simply loose all the knowledge, as mapToObj can not assume that the result is still sorted or distinct. 当它像.mapToObj(Long::valueOf)一样天真地实现时,它将简单地失去所有知识,因为mapToObj不能假设结果仍然是排序的或不同的。 But this has been fixed with Java 9. There, LongStream.range(0,10).boxed() has SUBSIZED|SIZED|ORDERED|SORTED|DISTINCT characteristics, maintaining all characteristics that have relevance to the implementation. 但是这已经用Java 9修复了。在那里, LongStream.range(0,10).boxed()具有SUBSIZED|SIZED|ORDERED|SORTED|DISTINCT特征,保留了与实现相关的所有特征。

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

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