深入了解分裂者的特征
Understanding deeply spliterator characteristics
为了尝试深入理解java流和分离器,我有一些关于分离器特性的微妙问题:
Q1:Stream.empty()
vs Stream.of()
(Stream.of() 无参数)
Stream.empty()
: 小号, 大号
Stream.of()
:已调整大小,不可更改,已调整大小,已订购
为什么 Stream.empty()
没有 Stream.of()
相同的特征?请注意,它在与 Stream.concat() 结合使用时会产生影响(特别是没有 ORDERED
)。我会说 Stream.empty()
不仅应该有 IMMUTABLE 和 ORDERED,还应该有 DISTINCT 和 NONNULL。它也有意义 Stream.of()
只有一个参数具有 DISTICT.
Q2:LongStream.of()
没有NONNULL
刚刚注意到 NONNULL 在 LongStream.of
中不可用。
NONNULL
不就是所有LongStream
、IntStream
和DoubleStream
的主要特征吗?
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()
:小号,大号,...
为什么 .peek()
会失去这些特征? .peek
不应该真的失去任何东西。
Q5: .skip()
, .limit()
丢失 SUBSIZED, IMMUTABLE, NONNULL, SIZED
注意这些操作丢失SUBSIZED、IMMUTABLE、NONNULL、SIZED。为什么?如果尺寸可用,那么最终尺寸也很容易计算。
Q6: .filter()
丢失 IMMUTABLE, NONNULL
请注意,此操作也会丢失 SUBSIZED、IMMUTABLE、NONNULL、SIZED。丢掉SUBSIZED和SIZED是有意义的,但是其他两个就没有意义了。为什么?
如果对拆分器有深刻理解的人能带来一些清晰度,我将不胜感激。谢谢。
我不得不承认,当我第一次尝试找出特征的实际含义时,我也遇到了困难,感觉在 Java 8 的实施阶段,它们的含义并没有明确确定,并且由于这个原因使用不一致。
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.
在此列表中看到“替换”很奇怪,在谈到 List
或数组时,通常不将其视为结构修改,因此,流和拆分器工厂接受数组(不是克隆)报告 IMMUTABLE
,例如 LongStream.of(…)
或 Arrays.spliterator(long[])
.
如果我们更慷慨地将其解释为“只要客户无法观察到”,则与 CONCURRENT
没有显着差异,因为在任何一种情况下 some元素将报告给客户端,但无法识别它们是在遍历期间添加的,还是由于删除而未报告的,因为无法倒带拆分器和比较。
规范继续:
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.
这是唯一相关的事情,报告 IMMUTABLE
或 CONCURRENT
的拆分器保证永远不会抛出 ConcurrentModificationException
。当然,CONCURRENT
在语义上排除了 SIZED
,但这对客户端代码没有影响。
事实上,这些特性在 Stream API 中没有用于任何东西,因此,不一致地使用它们永远不会在某处引起注意。
这也是为什么每个中间操作都有清除CONCURRENT
、IMMUTABLE
和NONNULL
特征的作用:流实现不使用它们,其表示流状态的内部 类 不维护它们。
同样,NONNULL
没有在任何地方使用,所以它对某些流没有影响。我可以将 LongStream.of(…)
问题追溯到 Arrays.spliterator(long[], int, int)
的内部使用,它委托给
Spliterators.spliterator(long[] array, int fromIndex, int toIndex, int additionalCharacteristics)
:
The returned spliterator always reports the characteristics SIZED
and SUBSIZED
. 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
). The method Arrays.spliterator(long[], int, int)
can often be used instead, which returns a spliterator that reports SIZED
, SUBSIZED
, IMMUTABLE
, and ORDERED
.
(再次)注意 IMMUTABLE
特性的不一致使用。它再次被视为必须保证没有任何修改,而与此同时,Arrays.spliterator
进而 Arrays.stream
和 LongStream.of(…)
将报告 IMMUTABLE
特征,即使根据规范,不能保证调用者不会修改他们的数组。除非我们考虑将一个元素设置为不进行结构修改,但是那样的话,整个区别又变得毫无意义了,因为数组不能进行结构修改。
而且它明确指定没有 NONNULL
特征。虽然很明显原始值不能是 null
,并且 Spliterator.Abstract<Primitive>Spliterator
类 总是注入 NONNULL
特征,但 Spliterators.spliterator(long[],int,int,int)
返回的拆分器不会继承自 Spliterator.AbstractLongSpliterator
.
糟糕的是,如果不更改规范就无法解决这个问题,好在无论如何都没有后果。
因此,如果我们忽略 CONCURRENT
、IMMUTABLE
或 NONNULL
中没有任何后果的任何问题,我们有
SIZED
和 skip
& limit
。这是一个众所周知的问题,是 Stream API 实现 skip
和 limit
方式的结果。其他实现是可以想象的。这也适用于无限流与 limit
的组合,它应该具有可预测的大小,但鉴于当前的实现,还没有。
合并 Stream.concat(…)
与 Stream.empty()
。空流 不会 对结果顺序施加约束,这听起来很合理。但是Stream.concat(…)
只有一个input没有订单时才放订单的行为,值得商榷。请注意,在排序方面过于激进并不是什么新鲜事,请参阅 关于一种首先被认为是故意的行为,但后来已被修复,直到 Java 8,更新 60。也许,Stream.concat
也应该在这个时候讨论…
.boxed()
的行为很容易解释。当它像 .mapToObj(Long::valueOf)
那样天真地实现时,它会简单地丢失所有知识,因为 mapToObj
不能假设结果仍然是有序的或不同的。但这已通过 Java 9 修复。LongStream.range(0,10).boxed()
具有 SUBSIZED|SIZED|ORDERED|SORTED|DISTINCT
特征,保留与实施相关的所有特征。
为了尝试深入理解java流和分离器,我有一些关于分离器特性的微妙问题:
Q1:Stream.empty()
vs Stream.of()
(Stream.of() 无参数)
Stream.empty()
: 小号, 大号Stream.of()
:已调整大小,不可更改,已调整大小,已订购
为什么 Stream.empty()
没有 Stream.of()
相同的特征?请注意,它在与 Stream.concat() 结合使用时会产生影响(特别是没有 ORDERED
)。我会说 Stream.empty()
不仅应该有 IMMUTABLE 和 ORDERED,还应该有 DISTINCT 和 NONNULL。它也有意义 Stream.of()
只有一个参数具有 DISTICT.
Q2:LongStream.of()
没有NONNULL
刚刚注意到 NONNULL 在 LongStream.of
中不可用。
NONNULL
不就是所有LongStream
、IntStream
和DoubleStream
的主要特征吗?
Q3:LongStream.range(,)
对比 LongStream.range(,).boxed()
LongRange.range(,)
:SUBSIZED,IMMUTABLE,NONNULL,SIZED,ORDERED,SORTED,DISTINCTLongStream.range(,).boxed()
:尺寸缩小,尺寸缩小,已订购
为什么 .boxed()
失去了所有这些特征?它不应该丢失任何东西。
我明白 .mapToObj()
可以失去 NONNULL、IMMUTABLE 和 DISTICT,但是 .boxed()
... 没有意义。
Q4:.peek()
丢失IMMUTABLE和NONNULL
LongStream.of(1)
: SUBSIZED, IMMUTABLE, NONNULL, SIZED, ...
LongStream.of(1).peek()
:小号,大号,...
为什么 .peek()
会失去这些特征? .peek
不应该真的失去任何东西。
Q5: .skip()
, .limit()
丢失 SUBSIZED, IMMUTABLE, NONNULL, SIZED
注意这些操作丢失SUBSIZED、IMMUTABLE、NONNULL、SIZED。为什么?如果尺寸可用,那么最终尺寸也很容易计算。
Q6: .filter()
丢失 IMMUTABLE, NONNULL
请注意,此操作也会丢失 SUBSIZED、IMMUTABLE、NONNULL、SIZED。丢掉SUBSIZED和SIZED是有意义的,但是其他两个就没有意义了。为什么?
如果对拆分器有深刻理解的人能带来一些清晰度,我将不胜感激。谢谢。
我不得不承认,当我第一次尝试找出特征的实际含义时,我也遇到了困难,感觉在 Java 8 的实施阶段,它们的含义并没有明确确定,并且由于这个原因使用不一致。
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.
在此列表中看到“替换”很奇怪,在谈到 List
或数组时,通常不将其视为结构修改,因此,流和拆分器工厂接受数组(不是克隆)报告 IMMUTABLE
,例如 LongStream.of(…)
或 Arrays.spliterator(long[])
.
如果我们更慷慨地将其解释为“只要客户无法观察到”,则与 CONCURRENT
没有显着差异,因为在任何一种情况下 some元素将报告给客户端,但无法识别它们是在遍历期间添加的,还是由于删除而未报告的,因为无法倒带拆分器和比较。
规范继续:
A Spliterator that does not report
IMMUTABLE
orCONCURRENT
is expected to have a documented policy (for example throwingConcurrentModificationException
) concerning structural interference detected during traversal.
这是唯一相关的事情,报告 IMMUTABLE
或 CONCURRENT
的拆分器保证永远不会抛出 ConcurrentModificationException
。当然,CONCURRENT
在语义上排除了 SIZED
,但这对客户端代码没有影响。
事实上,这些特性在 Stream API 中没有用于任何东西,因此,不一致地使用它们永远不会在某处引起注意。
这也是为什么每个中间操作都有清除CONCURRENT
、IMMUTABLE
和NONNULL
特征的作用:流实现不使用它们,其表示流状态的内部 类 不维护它们。
同样,NONNULL
没有在任何地方使用,所以它对某些流没有影响。我可以将 LongStream.of(…)
问题追溯到 Arrays.spliterator(long[], int, int)
的内部使用,它委托给
Spliterators.spliterator(long[] array, int fromIndex, int toIndex, int additionalCharacteristics)
:
The returned spliterator always reports the characteristics
SIZED
andSUBSIZED
. The caller may provide additional characteristics for the spliterator to report. (For example, if it is known the array will not be further modified, specifyIMMUTABLE
; if the array data is considered to have an encounter order, specifyORDERED
). The methodArrays.spliterator(long[], int, int)
can often be used instead, which returns a spliterator that reportsSIZED
,SUBSIZED
,IMMUTABLE
, andORDERED
.
(再次)注意 IMMUTABLE
特性的不一致使用。它再次被视为必须保证没有任何修改,而与此同时,Arrays.spliterator
进而 Arrays.stream
和 LongStream.of(…)
将报告 IMMUTABLE
特征,即使根据规范,不能保证调用者不会修改他们的数组。除非我们考虑将一个元素设置为不进行结构修改,但是那样的话,整个区别又变得毫无意义了,因为数组不能进行结构修改。
而且它明确指定没有 NONNULL
特征。虽然很明显原始值不能是 null
,并且 Spliterator.Abstract<Primitive>Spliterator
类 总是注入 NONNULL
特征,但 Spliterators.spliterator(long[],int,int,int)
返回的拆分器不会继承自 Spliterator.AbstractLongSpliterator
.
糟糕的是,如果不更改规范就无法解决这个问题,好在无论如何都没有后果。
因此,如果我们忽略 CONCURRENT
、IMMUTABLE
或 NONNULL
中没有任何后果的任何问题,我们有
SIZED
和 skip
& limit
。这是一个众所周知的问题,是 Stream API 实现 skip
和 limit
方式的结果。其他实现是可以想象的。这也适用于无限流与 limit
的组合,它应该具有可预测的大小,但鉴于当前的实现,还没有。
合并 Stream.concat(…)
与 Stream.empty()
。空流 不会 对结果顺序施加约束,这听起来很合理。但是Stream.concat(…)
只有一个input没有订单时才放订单的行为,值得商榷。请注意,在排序方面过于激进并不是什么新鲜事,请参阅 Stream.concat
也应该在这个时候讨论…
.boxed()
的行为很容易解释。当它像 .mapToObj(Long::valueOf)
那样天真地实现时,它会简单地丢失所有知识,因为 mapToObj
不能假设结果仍然是有序的或不同的。但这已通过 Java 9 修复。LongStream.range(0,10).boxed()
具有 SUBSIZED|SIZED|ORDERED|SORTED|DISTINCT
特征,保留与实施相关的所有特征。