从并行流

时间:2015-06-03 17:53:32

标签: java parallel-processing java-8 java-stream

我有一段这样的代码:

List<Egg> eggs = hens.parallelStream().map(hen -> {
    ArrayList<Egg> eggs = new ArrayList<>();
    while (hen.hasEgg()) {
        eggs.add(hen.getEgg());
    }
    return eggs;
}).flatMap(Collection::stream).collect(Collectors.toList());

但是通过这种方式,我必须为每只母鸡创建一个ArrayList,并且在母鸡100%处理之前不会收集鸡蛋。我想要这样的东西:

List<Egg> eggs = hens.parallelStream().map(hen -> {
    while (hen.hasEgg()) {
        yield return hen.getEgg();
    }
}).collect(Collectors.toList());

但是Java没有收益率。有没有办法实现它?

3 个答案:

答案 0 :(得分:8)

您的Hen课程很难适应Stream API。如果您无法更改它并且没有其他有用的方法(例如Collection<Egg> getAllEggs()Iterator<Egg> eggIterator()),您可以创建如下的蛋流:

public static Stream<Egg> eggs(Hen hen) {
    Iterator<Egg> it = new Iterator<Egg>() {
        @Override
        public boolean hasNext() {
            return hen.hasEgg();
        }

        @Override
        public Egg next() {
            return hen.getEgg();
        }
    };
    return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, 0), false);
}

现在您可以通过以下方式使用它:

List<Egg> eggs = hens.parallelStream()
                     .flatMap(hen -> eggs(hen))
                     .collect(Collectors.toList());

当然,如果您可以更改Stream类,则可以实现更好的Hen实施。

答案 1 :(得分:2)

使用hasEgg()getEgg()的迭代逻辑是有状态,因为这些方法的结果取决于之前的调用。因此,除非您设法完全更改接口,否则无法并行处理单个Hen

那就是说,你担心ArrayList是不必要的。当流实现并行执行collect操作时,它必须缓冲每个线程的值,然后组合这些缓冲区。甚至可能是操作根本没有从并行执行中受益。

您可以做的是将ArrayList替换为Stream.Builder,因为它针对仅在构建Stream之前添加的用例进行了优化:

List<Egg> eggs = hens.parallelStream().flatMap(hen -> {
    Stream.Builder<Egg> eggStream = Stream.builder();
    while(hen.hasEgg()) {
        eggStream.add(hen.getEgg());
    }
    return eggStream.build();
}).collect(Collectors.toList());

答案 2 :(得分:1)

假设存在getEggs()方法,您可以使用以下方法收集所有鸡蛋。

List<Egg> eggs = hens.parallelStream()
    .filter(Hen::hasEggs)
    .map(Hen::getEggs)
    .collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll);

代码假定getEggs()返回Collection。如果filter(Hen::hasEggs)getEggs()没有Collection时返回空Hen,则可以删除Eggs