简体   繁体   English

如何一次迭代 Stream 的两项?

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

I have a list of Vectors, between which I want to draw lines using a stream (with the goal being one continuous line going through all points).我有一个向量列表,我想在它们之间使用 stream 画线(目标是一条穿过所有点的连续线)。 Currently, my setup looks something like this:目前,我的设置如下所示:

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;
});

This unfortunately presents an obvious misuse of the reduce method, as I instead use it as way to iterate over each item twice, once for each neigbour in the stream.不幸的是,这明显滥用了 reduce 方法,因为我改为使用它来迭代每个项目两次,一次用于 stream 中的每个邻居。

Is there any way to achieve the same behaviour using some Binary Operator in the stream?有没有办法在 stream 中使用一些二元运算符来实现相同的行为? How could I emplement such Operator?我该如何实施这样的运营商?


As pointed out by @Piotr, using a generic for loop may just be the way to go.正如@Piotr 所指出的,使用通用 for 循环可能只是 go 的方法。 My solution to the problem is now as follows:我现在对这个问题的解决方案如下:

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);
}

Generally, the stream paradigm is built on a few fundamental ideas that don't quite fit the purpose of grouping items in the stream.通常,stream 范式是建立在一些不太适合 stream 中的项目分组目的的基本思想之上的。 The stream paradigm typically assumes that: stream 范例通常假设:

  • the stream can be split at arbitrary points for the purpose of parallelisation/caching; stream 可以在任意点拆分,以实现并行化/缓存;
  • reduction operations have the same basic semantics as addition (in other words, the ordering of two sequential items should not have an effect on the result of reduction, and the reduction operation itself can be split arbitrarily and the overall result will be the same).归约操作与加法具有相同的基本语义(换句话说,两个顺序项的顺序不应该对归约结果产生影响,归约操作本身可以任意拆分,总体结果将是相同的)。

If you really wanted to process items from a Stream in the way you suggest, then-- be it good practice or otherwise (spoiler: it's 'otherwise'...)-- you could write a method such as the following:如果您真的想以您建议的方式处理 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;
        }
    });
}

Notice that we have to go to some ugly lengths to allow one call into forEachOrdered() to remember state from the previous call, and for good reason: it generally breaks the functional programming paradigm to create "state" between separate calls in this way.请注意,我们必须将 go 调整到一些丑陋的长度,以允许一次调用 forEachOrdered() 以记住前一次调用中的 state,这是有充分理由的:它通常打破了函数式编程范式,以这种方式在单独的调用之间创建“状态”。 But the result is that you could take sequential pairs and combine into points as follows:但结果是您可以采用顺序对并组合成点,如下所示:

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

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

To the caller, so long as the forEachCombined() method is tucked away in a utility class with a warning 'not to try this at home', the resulting syntax arguably isn't too bad.对于调用者,只要将 forEachCombined() 方法隐藏在实用程序 class 中,并带有警告“不要在家尝试这个”,那么生成的语法可以说还不错。 It does break what many would see as good design principles, though.不过,它确实打破了许多人认为好的设计原则。

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

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