【问题标题】:Mapping Over an Ordered List in java 8在 java 8 中映射有序列表
【发布时间】:2016-03-21 21:17:05
【问题描述】:

在 Java 8 附带的流媒体库中,我们提供了 forEachOrdered API,其文档在此处复制

void forEachOrdered(消费者行为)

为此流的每个元素执行一个操作。

这是终端操作。

此操作一次处理一个元素,如果存在则按遇到顺序对一个元素执行操作发生在对后续元素执行操作之前,但对于任何给定元素,该操作可以在库选择的任何线程中执行。

我们对从map API 订购有任何保证吗?

例如,我在代码中有以下内容

private final BazUtils bazUtils;

public List<Foo> getFoos(List<Bar> bars) {
    AtomicInteger idx = new AtomicInteger(1);
    return bars.stream()
        .map(bar -> bazUtils.getFoo(bar, idx.getAndAdd(1)))
        .collect(Collectors.toList());
}

在处理过程中会遵守bars 的顺序吗?

【问题讨论】:

  • 我很难理解你所说的“接受一个元素及其在列表中的位置的函数”是什么意思,因为传递给Stream.map 的函数将只接收一个参数,而流不会给你一个职位。除此之外,您似乎混淆了 processing 顺序和 encounter 顺序。也许this 有点帮助(另请参阅 Stuart Marks 的评论)。
  • Holger,我可以为 map() 提供一个函数,它接受一个参数,但返回对另一个依赖多个参数的函数的调用。例如,{idx;地图(x -> foo(x, idx));}.
  • 当然,但是idxx 及其位置完全无关。
  • 如果idx 是您打算修改的堆变量,那么答案是,没有关于处理的保证对于并行流的顺序,该函数甚至可以同时进行评估。从映射函数中修改变量时,您可能会遇到竞争条件。如果中间步骤和终端动作都没有清除流的有序属性,则为有效的最终结果将保持 遭遇 顺序。
  • 您好 Holger,为了清楚起见,我在问题中的示例中添加了详细信息。

标签: java functional-programming java-8 java-stream


【解决方案1】:

https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps

如果流是有序的,则大多数操作都被限制为按照遇到的顺序对元素进行操作;如果一个流的源是一个包含[1, 2, 3]的List,那么执行map(x -> x*2)的结果一定是[2, 4, 6]。但是,如果源没有定义的遇到顺序,那么值 [2, 4, 6] 的任何排列都是有效的结果。

所以,是的;订单由map保留。

【讨论】:

    【解决方案2】:

    参见package documentation,“无状态行为”和“副作用”部分:

    ...如果行为参数确实有副作用,除非明确说明,否则无法保证这些副作用对其他线程的visibility,也不保证“相同”上的不同操作同一流管道中的元素在同一线程中执行。此外,这些影响的排序可能令人惊讶。即使管道被限制生成与流源的遇到顺序一致的 result(例如,IntStream.range(0,5).parallel().map(x -&gt; x*2).toArray() 必须生成 [0, 2, 4, 6, 8]),也不保证顺序其中 mapper 函数应用于单个元素,或者在哪个线程中为给定元素执行任何行为参数

    换句话说,即使使用AtomicInteger,您的示例在使用并行流时也不起作用,因为对于 处理 顺序没有任何保证。流将保持遭遇顺序,即生成的List 的顺序将反映原始Bars 在原始List 中的原始顺序,但int 值通过a 生成副作用可能不匹配(顺便说一下,并行性能可能很差)。

    forEachOrdered 终端动作非常特殊,动作的处理顺序会匹配遭遇顺序。使用此终端操作时,这将显着限制并行处理的好处。

    你可以正确地做你想做的事

    public List<Foo> getFoos(List<Bar> bars) {
        return IntStream.range(0, bars.size()) //you may add .parallel()
            .mapToObj(idx -> bazUtils.getFoo(bars.get(idx), idx+1))
            .collect(Collectors.toList());
    }
    

    【讨论】:

    • 太棒了。感谢您的解释和解决代码。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-11
    • 2016-02-12
    • 2015-12-27
    • 1970-01-01
    • 2017-01-28
    相关资源
    最近更新 更多