繁体   English   中英

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

[英]Understanding sequential vs parallel stream spliterators in Java 8 and Java 9

关于分裂者的问题乍一看并不简单。

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

之间的差异stream.spliterator() VS 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是从顺序流创建的,它禁用了拆分功能( .trySplit()返回null)。 稍后,需要转换回流,该流不会受益于并行处理。 丢人现眼。

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

// Supports activation of parallel processing later
public static <T> Stream<T> myOperation(Stream<T> stream) {
    boolean isParallel = stream.isParallel();
    Spliterator<T> spliterator = stream.parallel().spliterator();
    return StreamSupport.stream(new Spliterator<T>() {
        // 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的源分裂器。

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

同样

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

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

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

抽象包装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<String> sp = Stream.of("foo", "bar", "baz").map(x -> x).spliterator();
// null
System.out.println(sp.trySplit());

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

暂无
暂无

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

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