parallelStream与stream.parallel的区别

60

我一直对Collections.parallelStream()Collections.stream().parallel()之间的区别很好奇。根据Javadocs,parallelStream()试图返回并行流,而stream().parallel()返回并行流。通过自己的一些测试,我没有发现任何不同之处。这两种方法的区别在哪里?一个实现比另一个更时间高效吗?谢谢。


23
简短回答:没有区别。长篇回答:没有、没有、没有任何差别。它们只是彼此的别名。 - Louis Wasserman
我刚发现了一件事,我不知道它是否代表它们之间的差异?请参见https://dev59.com/MKHia4cB1Zd3GeqPZM1f - zhuguowei
3个回答

40
即使它们目前表现相同,在文档中有差别,正如你所指出的那样;据我所知,这可能会在将来被利用。
目前,在Collection接口中,parallelStream方法定义如下:
default Stream<E> parallelStream() {
    return StreamSupport.stream(spliterator(), true);
}

作为默认方法,它可以在实现中被覆盖 (这就是 Collections 内部类实际所做的)。

这暗示着即使默认方法返回一个并行流,也可能有集合覆盖此方法以返回一个非并行流。这可能是文档的原因。

同时,即使 parallelStream 返回一个串行流,它仍然是一个 Stream,你可以轻松地在其上调用 parallel

  Collections.some()
       .parallelStream() // actually sequential
       .parallel() // force it to be parallel

至少对我来说,这看起来很奇怪。

似乎文档应该以某种方式说明,在调用parallelStream之后,没有必要再次调用parallel来强制执行并行处理 - 因为这可能是无用的甚至对处理有害。

编辑

对于阅读此内容的任何人 - 请阅读Holger的评论;它涵盖了我在此答案中未提到的情况。


2
很难想象在不知道将要链接的实际操作的情况下,流源拒绝流的并行处理是合理的场景。也许,如果它正在滚动自己的Stream API实现,但那时,当调用.parallel()时,它完全控制发生的事情... - Holger
3
一个空的集合不会从并行处理中受益。然而,由于无论线程数量如何都不能得到保证,所以让isParallel()返回true也没有问题。同样地,如果一个单例集合上的流没有flatMap操作,它将永远不会使用第二个线程,但是,让isParallel()返回true也没有问题。这也适用于所有底层分裂器在trySplit中返回null的流;如果没有像sortedflatMap这样的操作,调用.parallel()也不重要,它不会产生任何影响。 - Holger
2
仔细思考一下,即使您有一个不支持任何并行处理的源,也没有理由禁止使用并行处理thatSource.parallelStream().sorted().otherOps(),一旦内部使用的数组已经被填充,因为此时处理完全与源分离。我有一种感觉,这个句子源于源和流实现之间的交互仍在进行的时期。 - Holger
2
@Holger,有时候我并不完全理解你的回答,但我一定会尝试去理解并加以修正... 你所说的“内部数组”是指这里的sorted操作,它会将元素从Spliterator复制到Sink(数组或ArrayList)中,对吗?很抱歉这么晚才提出这个问题。 - Eugene
2
是的,正如您所说,sorted() 使用的临时存储(无论是数组还是其他)与源集合无关,因此可以并行处理,即使源集合试图否认它。同样,如果分裂器的 trySplit 方法返回 null,则流实现可能会转向缓冲策略,就像 AbstractSpliterator.trySplit 所做的那样。实际上,如果流这样做而不是 AbstractSpliterator 更有意义... - Holger
显示剩余2条评论

10

Collections.parallelStream()Collections.stream().parallel()没有区别。它们都会根据底层分割器的限制来分割流,并且它们都将使用默认的ForkJoinPool(除非已经在另一个池内运行)。


3
class Employee {
    String name;
    int salary;

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public Employee(String name, int salary) {
        this.name = name;
        this.salary = salary;
    }
}
class ParallelStream {

    public static void main(String[] args) {

        long t1, t2;
        List<Employee> eList = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            eList.add(new Employee("A", 20000));
            eList.add(new Employee("B", 3000));
            eList.add(new Employee("C", 15002));
            eList.add(new Employee("D", 7856));
            eList.add(new Employee("E", 200));
            eList.add(new Employee("F", 50000));
        }

        /***** Here We Are Creating A 'Sequential Stream' & Displaying The Result *****/
        t1 = System.currentTimeMillis();
        System.out.println("Sequential Stream Count?= " + eList.stream().filter(e -> e.getSalary() > 15000).count());

        t2 = System.currentTimeMillis();
        System.out.println("Sequential Stream Time Taken?= " + (t2 - t1) + "\n");

        /***** Here We Are Creating A 'Parallel Stream' & Displaying The Result *****/
        t1 = System.currentTimeMillis();
        System.out.println("Parallel Stream Count?= " + eList.parallelStream().filter(e -> e.getSalary() > 15000).count());

        t2 = System.currentTimeMillis();
        System.out.println("Parallel Stream Time Taken?= " + (t2 - t1));

        /***** Here We Are Creating A 'Parallel Stream with Collection.stream.parallel' & Displaying The Result *****/
        t1 = System.currentTimeMillis();
        System.out.println("stream().parallel() Count?= " + eList.stream().parallel().filter(e -> e.getSalary() > 15000).count());

        t2 = System.currentTimeMillis();
        System.out.println("stream().parallel() Time Taken?= " + (t2 - t1));



    }

}

我已经尝试了所有三种方式.stream()、.parallelStream() 和 .stream().parallel(),使用相同数量的记录,并且能够确定这三种方法所花费的时间。以下是我提到的输出结果。
Sequential Stream Count?= 300
Sequential Stream Time Taken?= 18
Parallel Stream Count?= 300
Parallel Stream Time Taken?= 6
stream().parallel() Count?= 300
stream().parallel() Time Taken?= 1

我不确定,但是如上所述,stream().parallel()的执行时间是parallelStream()的六分之一。

如果有专家建议,仍然非常欢迎。


如果您单独运行流,可以看到流的时间几乎相同。 - Premraj
尝试将循环大小从100更改为5_000_000,然后您将看到另一个结果如下:Sequential Stream Count?= 15000000 Sequential Stream Time Taken?= 102 Parallel Stream Count?= 15000000 Parallel Stream Time Taken?= 64 stream().parallel() Count?= 15000000 stream().parallel() Time Taken?= 97 - Ali Sadeghi
请记住,Java运行时使用JIT,因此像这样进行基准测试会更加棘手。 - vadipp
循环大小100,000结果第一次调用:-`顺序流计数?= 300000 顺序流所需时间?= 52并行流计数?= 300000 并行流所需时间?= 40stream().parallel() 计数?= 300000 stream().parallel() 所需时间?= 29`第二次调用:-`顺序流计数?= 300000 顺序流所需时间?= 25并行流计数?= 300000 并行流所需时间?= 47stream().parallel() 计数?= 300000 stream().parallel() 所需时间?= 37` - Rahim Khan Abdul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接