繁体   English   中英

如何一次迭代 Stream 的两项?

[英]How can I iterate over two items of a Stream at once?

我有一个向量列表,我想在它们之间使用 stream 画线(目标是一条穿过所有点的连续线)。 目前,我的设置如下所示:

l.points().stream()
            .map(v -> vectorOfTile((int) v.x, (int) v.y))
            .reduce(l.points().get(0), (v1, v2) -> {
                line(v1.x, v1.y, v2.x, v2.y);
                return v2;
});

不幸的是,这明显滥用了 reduce 方法,因为我改为使用它来迭代每个项目两次,一次用于 stream 中的每个邻居。

有没有办法在 stream 中使用一些二元运算符来实现相同的行为? 我该如何实施这样的运营商?


正如@Piotr 所指出的,使用通用 for 循环可能只是 go 的方法。 我现在对这个问题的解决方案如下:

PVector[] a = l.points().stream().map(v -> vectorOfTile((int) v.x, (int) v.y)).toArray(PVector[]::new);
for (int i = 0; i < a.length - 1; i++) {
    line(a[i].x, a[i].y, a[i + 1].x, a[i + 1].y);
}

通常,stream 范式是建立在一些不太适合 stream 中的项目分组目的的基本思想之上的。 stream 范例通常假设:

  • stream 可以在任意点拆分,以实现并行化/缓存;
  • 归约操作与加法具有相同的基本语义(换句话说,两个顺序项的顺序不应该对归约结果产生影响,归约操作本身可以任意拆分,总体结果将是相同的)。

如果您真的想以您建议的方式处理 Stream 中的项目,那么 - 无论是好习惯还是其他方式(剧透:它是“否则”......) - 您可以编写如下方法:

public static <T, V> void forEachCombined(Stream<T> sourceStream, BiFunction<T, T, V> combiner, Consumer<V> op) {
    assert(!sourceStream.isParallel());

    T[] pair = (T[]) new Object[2];
    int[] pos = new int[1];

    sourceStream.forEachOrdered(obj -> {
        pair[pos[0]] = obj;
        if (++pos[0] == 2) {
            V combined = combiner.apply(pair[0], pair[1]);
            op.accept(combined);
            pos[0] = 0;
        }
    });
}

请注意,我们必须将 go 调整到一些丑陋的长度,以允许一次调用 forEachOrdered() 以记住前一次调用中的 state,这是有充分理由的:它通常打破了函数式编程范式,以这种方式在单独的调用之间创建“状态”。 但结果是您可以采用顺序对并组合成点,如下所示:

    Stream<Integer> coordStream = Stream.of(1, 2, 3, 4);

    forEachCombined(coordStream, Point::new, point -> {
        // ... Do something with 'point'
    });

对于调用者,只要将 forEachCombined() 方法隐藏在实用程序 class 中,并带有警告“不要在家尝试这个”,那么生成的语法可以说还不错。 不过,它确实打破了许多人认为好的设计原则。

暂无
暂无

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

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