66

为了尝试深入理解 java 流和拆分器,我对拆分器特性有一些微妙的问题:

Q1:Stream.empty()vsStream.of()(没有参数的Stream.of())

  • Stream.empty():缩小的, 大小的
  • Stream.of(): SUBSIZED, IMMUTABLE , SIZED, ORDERED

为什么Stream.empty()没有相同的特征Stream.of()?请注意,与 Stream.concat() 结合使用时会产生影响(特别是没有ORDERED)。我想说这Stream.empty()不仅应该有IMMUTABLE 和 ORDERED,还应该有DISTINCT 和 NONNULLStream.of()只有一个参数具有DISTICT 也是有意义的。

Q2:LongStream.of()没有NONNULL

刚刚注意到 NONNULL 在LongStream.of. 不是NONNULL所有LongStreams,IntStreams和DoubleStreams的主要特征吗?

Q3:LongStream.range(,)对比LongStream.range(,).boxed()

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

为什么.boxed()会失去所有这些特征?它不应该失去任何东西。

我知道这.mapToObj()可能会丢失NONNULL、IMMUTABLE 和 DISTICT,但是.boxed()......没有意义。

Q4:.peek()丢失IMMUTABLE 和 NONNULL

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

为什么.peek()会失去这些特征?.peek不应该真的失去任何东西。

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

请注意,这些操作会丢失SUBSIZED, IMMUTABLE, NONNULL, SIZED。为什么?如果尺寸可用,那么计算最终尺寸也很容易。

Q6:.filter()丢失IMMUTABLE, NONNULL

请注意,此操作也会丢失SUBSIZED, IMMUTABLE, NONNULL, SIZED。丢失SUBSIZED 和 SIZED是有意义的,但其他两个没有意义。为什么?


如果深入了解拆分器的人可以带来一些清晰度,我将不胜感激。谢谢。

4

1 回答 1

46

不得不承认,当我第一次试图找出特性的实际含义时,我也遇到了困难,并且感觉它们的含义在 Java 8 的实现阶段并没有明确地确定,因此使用不一致。

考虑Spliterator.IMMUTABLE

表示元素源不能进行结构修改的特征值;也就是说,元素不能被添加、替换或删除,因此在遍历过程中不会发生这种变化。

List在这个列表中看到“replaced”很奇怪,当谈到 a or 数组时,通常不将其视为结构修改,因此,流和拆分器工厂接受数组(未克隆)报告IMMUTABLE,如LongStream.of(…)or Arrays.spliterator(long[])

如果我们更宽泛地将其解释为“只要客户端无法观察到”,则CONCURRENT与有些由于删除而未报告,因为没有办法倒带拆分器并进行比较。

规范继续:

不报告IMMUTABLE或预期具有与遍历期间检测到的结构干扰有关CONCURRENT的记录策略(例如 throwing )的 Spliterator。ConcurrentModificationException

这是唯一相关的事情,分裂器报告要么,IMMUTABLE要么CONCURRENT,保证永远不会抛出ConcurrentModificationException. 当然,从语义上CONCURRENT排除SIZED,但这对客户端代码没有影响。

事实上,这些特性并没有用于 Stream API 中的任何内容,因此,不一致地使用它们永远不会在某处引起注意。

这也是为什么每个中间操作都具有清除CONCURRENT,IMMUTABLENONNULL特性的效果的解释:Stream 实现不使用它们并且其表示流状态的内部类不维护它们。


同样,NONNULL不在任何地方使用,因此对于某些流没有它没有任何影响。我可以将LongStream.of(…)问题追溯到内部使用Arrays.spliterator(long[], int, int)哪些代表
Spliterators.spliterator​(long[] array, int fromIndex, int toIndex, int additionalCharacteristics)

返回的拆分器始终报告特征SIZEDSUBSIZED。调用者可以为拆分器提供额外的特性来报告。(例如,如果已知数组不会被进一步修改,则指定IMMUTABLE;如果认为数组数据有遇到顺序,则指定ORDERED)。该方法Arrays.spliterator(long[], int, int)通常可以改为使用,它返回一个拆分器,用于报告SIZEDSUBSIZEDIMMUTABLEORDERED

注意(再次)该IMMUTABLE特性的不一致使用。它再次被视为必须保证没有任何修改,同时,Arrays.spliterator反过来Arrays.streamLongStream.of(…)将报告IMMUTABLE特征,即使按照规范,也不能保证调用者不会修改他们的数组。除非我们考虑将元素设置为不进行结构修改,否则整个区别再次变得毫无意义,因为数组不能进行结构修改。

它明确指出没有NONNULL特征。虽然很明显原始值不能是null,并且Spliterator.Abstract<Primitive>Spliterator类总是注入一个NONNULL特征,但返回的拆分Spliterators.spliterator​(long[],int,int,int)器不会继承自Spliterator.AbstractLongSpliterator.

不好的是,这不能在不更改规范的情况下解决,好的是,无论如何它没有任何后果。


因此,如果我们忽略、 或的任何没有后果的问题CONCURRENT,我们就有IMMUTABLENONNULL

SIZEDskip& limit。这是一个众所周知的问题,skiplimitStream API 实现的结果。其他实现是可以想象的。这也适用于无限流与 a 的组合limit,它应该具有可预测的大小,但鉴于当前的实现,还没有。

结合. Stream.concat(…)_ Stream.empty()空流不会对结果顺序施加约束听起来很合理。但是Stream.concat(…),当只有一个输入没有顺序时释放顺序的行为是值得怀疑的。请注意,在订购方面过于激进并不是什么新鲜事,请参阅此 Q&A关于首先被认为是故意的行为,但后来在 Java 8 更新 60 中得到了修复。也许,Stream.concat此时也应该讨论一下…</p>

的行为.boxed()很容易解释。当它被天真地实现时.mapToObj(Long::valueOf),它会简单地丢失所有知识,因为mapToObj不能假设结果仍然是排序的或不同的。但这已在 Java 9 中得到解决。有一些LongStream.range(0,10).boxed()特性SUBSIZED|SIZED|ORDERED|SORTED|DISTINCT,维护与实现相关的所有特性。

于 2017-10-09T14:51:52.070 回答