了解Java 8和Java 9中的顺序流和并行流分裂器

关于劈开者的问题乍一看并不简单。

在流中, .parallel()更改流处理的行为。 但是我期望顺序和并行流创建的分裂器是相同的。 例如, 在顺序流中,通常不会调用.trySplit() ,而在并行流中,它是为了将拆分分裂器移交给另一个线程。

stream.parallel().spliterator()stream.parallel().spliterator()之间的差异:

  1. 它们可能具有不同的特征:

     Stream.of(1L, 2L, 3L).limit(2); // ORDERED Stream.of(1L, 2L, 3L).limit(2).parallel(); // SUBSIZED, SIZED, ORDERED 

这里讨论了另一个无意义的流分裂器特征策略(并行似乎更好地计算): 了解java 8和java 9中的深层分裂器特征

  1. 它们在使用.trySplit()分割方面可能有不同的行为:

     Stream.of(1L, 2L, 3L); // NON NULL Stream.of(1L, 2L, 3L).limit(2); // NULL Stream.of(1L, 2L, 3L).limit(2).parallel(); // NON NULL 

为什么最后两个有不同的行为? 如果我愿意,为什么我不能拆分连续流? (例如,丢弃其中一个分割以进行快速处理可能很有用)。

  1. 将分裂器转换为流时的重大影响:

     spliterator = Stream.of(1L, 2L, 3L).limit(2).spliterator(); stream = StreamSupport.stream(spliterator, true); // No parallel processing! 

在这种情况下,spliterator是从顺序流创建的,它禁用了拆分function( .trySplit()返回null)。 稍后,需要转换回流,该流不会受益于并行处理。 丢人现眼。

最大的问题:作为一种解决方法,在调用.spliterator()之前始终将流转换为并行的主要影响是什么?

 // Supports activation of parallel processing later public static  Stream myOperation(Stream stream) { boolean isParallel = stream.isParallel(); Spliterator spliterator = stream.parallel().spliterator(); return StreamSupport.stream(new Spliterator() { // My implementation of the interface here (omitted for clarity) }, isParallel).onClose(stream::close); } // Now I have the option to use parallel processing when needed: myOperation(stream).skip(1).parallel()... 

这不是分裂器的一般属性,而是仅包装封装流管道的分裂器。

当您在从spliterator生成并且没有链接操作的流上调用spliterator()时,无论流parallel状态如何,您都将获得可能支持或不支持trySplit的源spliterator。

 ArrayList list = new ArrayList<>(); Collections.addAll(list, "foo", "bar", "baz"); Spliterator sp1 = list.spliterator(), sp2=list.stream().spliterator(); // true System.out.println(sp1.getClass()==sp2.getClass()); // not null System.out.println(sp2.trySplit()); 

同样

 Spliterator sp = Stream.of("foo", "bar", "baz").spliterator(); // not null System.out.println(sp.trySplit()); 

但是只要在调用spliterator()之前链接操作,就会得到一个包含流管道的分裂器。 现在,可以实现执行相关操作的专用分裂器,如LimitSpliteratorMappingSpliterator ,但这还没有完成,因为当其他终端操作没有时,将流转换回分裂器已被视为最后的手段适合,而不是高优先级的用例。 相反,您将始终获得单个实现类的实例,该实例类尝试将流管道实现的内部工作转换为spliterator API。

对于有状态操作,这可能是安静的,对于非SIZED流,最明显的是,已sorteddistinctskiplimit 。 对于简单的无状态操作,比如mapfilter ,提供支持要容易得多,就像在代码注释中所说的那样

抽象包装spliterator,在第一次操作时绑定到管道助手的spliterator。 这个分裂器不是后期绑定的,并且在第一次操作时将绑定到源分裂器。 如果存在有状态操作,则不能拆分由顺序流生成的包裹分裂器。

 … // @@@ Detect if stateful operations are present or not // If not then can split otherwise cannot /** * True if this spliterator supports splitting */ final boolean isParallel; 

但似乎目前这种检测尚未实施,所有中间操作都被视为有状态操作。

 Spliterator sp = Stream.of("foo", "bar", "baz").map(x -> x).spliterator(); // null System.out.println(sp.trySplit()); 

当您尝试通过始终调用parallel来解决此问题时,当流管道仅包含无状态操作时,将不会产生任何影响。 但是当进行有状态操作时,它可能会显着改变行为。 例如,当您有一个已sorted步骤时,所有元素都必须进行缓冲和排序,然后才能使用第一个元素。 对于并行流,它可能会使用parallelSort ,即使您从未调用trySplit