【问题标题】:How can I iterate over two items of a Stream at once?如何一次迭代 Stream 的两个项目?
【发布时间】:2021-05-21 21:14:54
【问题描述】:

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

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 方法的误用,因为我使用它来对每个项目进行两次迭代,对流中的每个邻居进行一次。

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


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

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

【问题讨论】:

  • 我认为您的问题假设流既是顺序的又是有序的(有些不是)。我猜API只支持适用于所有类型流的操作,所以可能没有API可以从流中获取“一对相邻项”,因为它对于并行或无序流没有很好的定义?也许你可以为此做一个普通的 for 循环?会不会容易些?
  • @Piotr 好点 - 我主要使用顺序流,所以这是我没有考虑过的。我认为您可能是对的,我最好的方法是常规的 for 循环。我现在刚刚将映射流变成了一个数组,它以良好的易读性做了同样的事情。谢谢!
  • 从学校给我一个怀旧的回忆。是的,当使用同一集合中的多个元素序列时,使用常规 for 循环会更容易。试图使用流将奇数和偶数相加为同一流中的两个不同结果。流不能很好地访问多个元素的序列。

标签: java java-stream processing


【解决方案1】:

一般来说,流范式是建立在一些不太符合在流中对项目进行分组的目的的基本思想之上的。流范式通常假设:

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

如果您真的想按照您建议的方式处理 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;
        }
    });
}

请注意,我们必须竭尽全力允许一次调用 forEachOrdered() 来记住前一次调用的状态,这是有充分理由的:它通常会破坏函数式编程范式以在不同调用之间创建“状态”这边走。但结果是您可以采用顺序对并组合成点,如下所示:

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

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

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

【讨论】:

    猜你喜欢
    • 2012-07-06
    • 2021-10-05
    • 1970-01-01
    • 1970-01-01
    • 2015-09-04
    • 1970-01-01
    • 2018-04-04
    • 2022-11-17
    相关资源
    最近更新 更多