Mapping Over an Ordered List in java 8

老子叫甜甜 提交于 2019-12-08 06:02:12

问题


In the streaming library included with Java 8, we're provided with the forEachOrdered API whose documentation is reproduced here

void forEachOrdered(Consumer action)

Performs an action for each element of this stream.

This is a terminal operation.

This operation processes the elements one at a time, in encounter order if one exists. Performing the action for one element happens-before performing the action for subsequent elements, but for any given element, the action may be performed in whatever thread the library chooses.

Do we have any guarantees in terms of ordering from the map API?

For example, I have the following in code

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

Will the order of bars be respected in processing?


回答1:


See the package documentation, sections “Stateless behaviors” and “Side-effects”:

…If the behavioral parameters do have side-effects, unless explicitly stated, there are no guarantees as to the visibility of those side-effects to other threads, nor are there any guarantees that different operations on the "same" element within the same stream pipeline are executed in the same thread. Further, the ordering of those effects may be surprising. Even when a pipeline is constrained to produce a result that is consistent with the encounter order of the stream source (for example, IntStream.range(0,5).parallel().map(x -> x*2).toArray() must produce [0, 2, 4, 6, 8]), no guarantees are made as to the order in which the mapper function is applied to individual elements, or in what thread any behavioral parameter is executed for a given element

In other words, your example won’t work when using a parallel stream, even using an AtomicInteger as there is no guaranty regarding the processing order. The stream will maintain the encounter order, i.e. the resulting List’s order will reflect the original order of the Bars in the original List but the int values generated via a side effect may not match (and the parallel performance might be poor, by the way).

The forEachOrdered terminal action is very special in that the action’s processing order will match the encounter order. This will limit the benefit of parallel processing dramatically when using this terminal action.

You can do what you want correctly using

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



回答2:


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

If a stream is ordered, most operations are constrained to operate on the elements in their encounter order; if the source of a stream is a List containing [1, 2, 3], then the result of executing map(x -> x*2) must be [2, 4, 6]. However, if the source has no defined encounter order, then any permutation of the values [2, 4, 6] would be a valid result.

So, yes; order is preserved by map.



来源:https://stackoverflow.com/questions/34300121/mapping-over-an-ordered-list-in-java-8

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!