Java 8:通过检查所有Stream元素来停止减少操作

qua*_*tum 18 java reduce fold java-8 java-stream

我试图了解是否有办法终止还原操作而不检查整个流,我无法找到方法.

用例大致如下:让一长串的Integers需要折叠成一个Accumulator.每个元素检查都可能很昂贵,所以在内部Accumulator,我对传入进行检查Accumulator,看看我们是否需要执行昂贵的操作 - 如果我们不这样做,那么我只需返回累加器.

对于小(呃)列表来说,这显然是一个很好的解决方案,但是巨大的列表会产生我不想要的不必要的流元素访问成本.

这是一个代码草图 - 仅假设连续减少.

class Accumulator {
    private final Set<A> setA = new HashSet<>;
    private final Set<B> setB = new HashSet<>;
}

class ResultSupplier implements Supplier<Result> {

    private final List<Integer> ids;

    @Override
    public Result get() {
        Accumulator acc = ids.stream().reduce(new Accumulator(), f(), (x, y) -> null);

        return (acc.setA.size > 1) ? Result.invalid() : Result.valid(acc.setB);
    }

    private static BiFunction<Accumulator, Integer, Accumulator> f() {
        return (acc, element) -> {
            if (acc.setA.size() <= 1) {
                // perform expensive ops and accumulate results
            }
            return acc;
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

除了必须遍历整体之外Stream,还有一个我不喜欢的事实 - 我必须检查两次相同的条件(即setA尺寸检查).

我已经考虑过map()并且collect()操作但它们看起来更像是相同的并且没有发现它们实质上改变了我无法在不检查整个流的情况下完成折叠操作的事实.

另外,我的想法是假想的takeWhile(p : (A) => boolean)Stream API通讯员也不会给我们买任何东西,因为终止条件取决于累加器,而不是流元素本身.

请记住,我是FP的相对新人,所以有没有办法让这项工作像我期望的那样?我是否未正确设置整个问题或设计是否存在此限制?

the*_*472 8

而不是从ids.stream()你开始

  1. 使用 ids.spliterator()
  2. 将得到的spliterator包装到具有volatile布尔标志的自定义spliterator中
  3. tryAdvance如果标志被更改,则自定义spliterator 返回false
  4. 将您的自定义分裂器转换为流 StreamSupport.stream(Spliterator<T>, boolean)
  5. 像以前一样继续你的流管道
  6. 当累加器已满时,通过切换布尔值来关闭流

添加一些静态辅助方法以使其保持功能.

生成的API可以看看这个

Accumulator acc = terminateableStream(ids, (stream, terminator) ->
   stream.reduce(new Accumulator(terminator), f(), (x, y) -> null));
Run Code Online (Sandbox Code Playgroud)

另外,我的想法是想象中的takeWhile(p:(A)=> boolean)Stream API通讯员也会给我们什么都没买

如果条件依赖于累加器状态而不是流成员,它确实有效.这基本上就是我上面概述的方法.

它可能takeWhile在JDK提供的内容中被禁止,但使用分裂器的自定义实现可以采用有状态的方法.


Luk*_*der 5

当然,会有一个有趣的,纯粹的FP答案,可能有助于以你想要的方式解决这个问题.

与此同时,为什么当简单的解决方案在实际上是必要的并且你的原始数据源是List无论如何已经完全具体化时,为什么要使用FP ,你将使用串行缩减,而不是并行缩减.写这个:

@Override
public Result get() {
    Accumulator acc = new Accumulator();

    for (Integer id : ids) {
        if (acc.setA.size() <= 1) {
            // perform expensive ops and accumulate results
        }

        // Easy:
        if (enough)
            break;
    }

    return (acc.setA.size > 1) ? Result.invalid() : Result.valid(acc.setB);
}
Run Code Online (Sandbox Code Playgroud)

  • 什么是纯FP答案?:) (3认同)