深入了解分路器特性

2022-08-31 15:02:35

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

Q1: Stream.empty() vs Stream.of() (Stream.of() 不带 args)

  • Stream.empty()尺寸, 尺寸
  • Stream.of()子大小、不可变、大小、有序

为什么不具有相同的特征?请注意,当与 Stream.concat() 结合使用时,它会产生影响(特别是没有 )。我想说的是,这不仅应该是不可变的和有序的,而且还应该有独特的和非空洞的。此外,只有一个参数具有DISTICT,这是有道理的。Stream.empty()Stream.of()ORDEREDStream.empty()Stream.of()

Q2: LongStream.of() 没有 NONNULL

刚刚注意到 NONNULL 在 中不可用。不是所有s,s和s的主要特征吗?LongStream.ofNONNULLLongStreamIntStreamDoubleStream

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

  • LongRange.range(,)子大小、不可变、非空心、大小、有序、排序、不同
  • LongStream.range(,).boxed()尺寸, 尺寸, 有序

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

我知道可以失去NONNULL,IMMUTABLE和DISTICT,但是...没有道理。.mapToObj().boxed()

问题 4:.peek() 丢失了 IMMUTABLE 和 NONNULL

LongStream.of(1)SUBSIZE, IMMUTABLE, NONNULL, SIZE, ...SUBSIZE, SIZE, ...LongStream.of(1).peek()

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

Q5: .skip().limit() 丢失 SUBSIZE, IMMUTABLE, NONNULL, SIZE

请注意,这些操作丢失了 SUBSIZE、IMMUTABLE、NONNULL、SIZE。为什么?如果大小可用,则也很容易计算最终大小。

问题 6:.filter() 丢失不可变、非空

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


如果一个深刻理解分离器的人可以带来一些清晰度,我将不胜感激。谢谢。


答案 1

我不得不承认,当我第一次试图找出这些特征的实际含义时,我也遇到了困难,并且感觉它们的含义在Java 8的实现阶段没有明确解决,并且由于这个原因使用不一致。

考虑 Spliterator.IMMUTABLE

表示元素源不能在结构上修改的特征值;也就是说,无法添加、替换或删除元素,因此在遍历期间不会发生此类更改。

在此列表中看到“替换”是很奇怪的,当谈到数组或数组时,这通常不被视为结构修改,因此,流和拆分器工厂接受数组(未克隆)报告,如 或 。ListIMMUTABLELongStream.of(…)Arrays.spliterator(long[])

如果我们更慷慨地将其解释为“只要客户端无法观察到”,则与 没有显着差异,因为在任何一种情况下,某些元素都将报告给客户端,而没有任何方法可以识别它们是否在遍历期间添加,或者某些元素是否由于删除而未报告,因为没有办法倒带拆分器并进行比较。CONCURRENT

规范继续说:

不报告或预期具有记录的有关遍历期间检测到结构干扰的策略(例如抛出)的拆分器。IMMUTABLECONCURRENTConcurrentModificationException

这是唯一相关的事情,拆分器报告或,保证永远不会抛出.当然,在语义上排除了,但这对客户端代码没有任何影响。IMMUTABLECONCURRENTConcurrentModificationExceptionCONCURRENTSIZED

实际上,这些特征不用于流 API 中的任何内容,因此,不一致地使用它们永远不会在某个地方引起注意。

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


同样,不在任何地方使用,因此对于某些流,它没有效果。我可以追踪到内部使用哪些委托给
Spliterators.spliterator(long[]array,int fromIndex,int toIndex,int additionalCharacteristics)NONNULLLongStream.of(…)Arrays.spliterator(long[], int, int)

返回的拆分器始终报告特征 和 。调用方可以提供其他特征供拆分器报告。(例如,如果已知数组不会进一步修改,请指定;如果数组数据被视为具有遭遇顺序,请指定 )。通常可以改用该方法,它返回一个分离器,该拆分器报告 、 、 和 。SIZEDSUBSIZEDIMMUTABLEORDEREDArrays.spliterator(long[], int, int)SIZEDSUBSIZEDIMMUTABLEORDERED

注意(再次)使用特性的不一致。它再次被视为必须保证没有任何修改,同时,反过来,并且将报告特征,即使通过规范,也无法保证调用方不会修改其数组。除非我们考虑将元素设置为不是结构修改,否则整个区别再次变得荒谬,因为数组不能在结构上进行修改。IMMUTABLEArrays.spliteratorArrays.streamLongStream.of(…)IMMUTABLE

而且它明确没有指定任何特征。虽然很明显基元值不能是 ,并且类总是注入特征,但 返回的拆分器不会从 继承。NONNULLnullSpliterator.Abstract<Primitive>SpliteratorNONNULLSpliterators.spliterator​(long[],int,int,int)Spliterator.AbstractLongSpliterator

坏处是,如果不改变规格就无法解决这个问题,好处是,反正没有后果。


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

SIZED和 .这是一个众所周知的问题,是该方法的结果,并且已由流 API 实现。其他实现是可以想象的。这也适用于无限流与 的组合,它应该具有可预测的大小,但考虑到当前的实现,没有。skiplimitskiplimitlimit

与 组合使用。空流不会对结果顺序施加约束,这听起来是合理的。但是,当只有一个输入没有订单时,释放订单的行为是值得怀疑的。请注意,在排序方面过于激进并不是什么新鲜事,请参阅此问答,了解首先被认为是有意的行为,但后来在Java 8更新60中得到了修复。也许,在这个时间点上也应该讨论过...Stream.concat(…)Stream.empty()Stream.concat(…)Stream.concat

的行为很容易解释。当它像天真地实现一样,它只会失去所有的知识,因为不能假设结果仍然是排序或不同的。但是 Java 9 已经修复了这个问题。在那里,具有特征,保持与实施相关的所有特征。.boxed().mapToObj(Long::valueOf)mapToObjLongStream.range(0,10).boxed()SUBSIZED|SIZED|ORDERED|SORTED|DISTINCT


答案 2

推荐