簡體   English   中英

減少列表<completablefuture<t> &gt; </completablefuture<t>

[英]Reduce List<CompletableFuture<T>>

當給出ints時:

List<Integer> ints = IntStream.range(0, 1000).boxed().collect(Collectors.toList());

使用 Java Stream API,我們可以減少它們

MyValue myvalue = ints
        .parallelStream()
        .map(x -> toMyValue(x))
        .reduce((t, t2) -> t.combine(t2))
        .get();

在這個例子中,對我來說重要的是......

  • 項目將在多個線程中減少
  • 早期映射的項目將提前減少
  • 並非toMyValue()的所有結果都會同時加載

現在我想通過CompletableFuture API 做同樣的處理。

要做 map,我做了:

List<CompeletableFuture<MyValue>> myValueFutures = ints
        .stream()
        .map(x -> CompletableFuture.supplyAsync(() -> toMyValue(x), MY_THREAD_POOL))
        .collect(Collectors.toList());

現在我不知道如何減少List<CompeletableFuture<MyValue>> myValueFutures以獲得單個MyValue

並行 stream 提供了方便的 API,但由於這些問題,我不想使用 Stream API:

  • 並行 stream 在處理過程中很難停止階段。
  • 當某些工作人員被 IO 阻塞時,並行流的活動工作人員計數可能會超過並行度。 這有助於最大限度地提高 CPU 利用率,但可能會出現 memory 開銷(甚至 OOM)。

有什么方法可以減少 CompetableFutures? 一一去掉 stream 減少 api?

有趣的是,在您的初始示例中,您已經提到了一個方法combine ,而CompletableFuture有一個專用方法,僅用於此: thenCombine (及其兩個兄弟thenCombineAsync )。

所以考慮到你有類似的東西:

static class MyValue {
    final int x;

    MyValue(int x) {
        this.x = x;
    }

    MyValue combine(MyValue v){
        return new MyValue(this.x + v .x);
    }
}

static MyValue toMyValue(int x) {
    return new MyValue(x);
}

和:

List<CompletableFuture<Integer>> list = 
    IntStream.range(0, 4)
             .mapToObj(x -> supplyAsync(() -> x))
             .collect(Collectors.toList());

您可以使用其中一種thenCombine方法並通過以下方式實現您想要的:

MyValue value =
    list.stream()
        .map(x -> x.thenApply(YourClass::toMyValue))
        .reduce((left, right) -> left.thenCombine(right, MyValue::combine))
        .orElse(CompletableFuture.completedFuture(new MyValue(0)))
        .join();

如果要在可預測的線程中執行組合操作,則需要一個池或重載方法,例如:

.reduce((left, right) -> left.thenCombineAsync(right, MyValue::combine, MY_THREAD_POOL))

基本上,您需要等待所有CompletableFuture的結果,然后組合以獲得所需的結果。

為此有多種方法,但CompletableFuture allOf提供了可用於該目的的 allOf 方法。

當我不得不處理類似的問題時,我喜歡遵循 Tomasz Nurkiewicz 的建議並以下列方式執行這種計算。

正如文章中所建議的,首先,讓我們定義以下方便的方法: allOf接收 arguments 作為可變參數,並且不返回聚合結果的未來; 此方法將允許您克服這些缺點,因此您可以將Collection作為參數傳遞並返回實際結果的List而不是Void

private static <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> futures) {
    CompletableFuture<Void> allDoneFuture =
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
    return allDoneFuture.thenApply(v ->
            futures.stream().
                    map(future -> future.join()).
                    collect(Collectors.<T>toList())
    );
}

使用這種方便的方法,您可以通過以下方式減少您的值:

final CompletableFuture<List<MyValue>> allDone = sequence(myValueFutures);

// Please, see also for alternate approaches
// https://stackoverflow.com/questions/43489281/return-value-directly-from-completablefuture-thenaccept
final List<MyValue> myValues = allDone.join();

final Optional<MyValue> optResult = myValues.stream().
  reduce((t, t2) -> t.combine(t2))
;

// Process the returned value as you consider appropriate
final MyValue result = optResult.get();
    static BiFunction<Airline,Integer,Double> getTotalDelay=(airline,year)-> airline
        .getFlights().stream()
        .filter(flight ->flight.getScheduledDeparture().getYear()==year)
        .mapToDouble(f->calcFlightDelay.apply(f)).average().orElse(0.0);


//todo: Implement the following function
static TriFunction<List<Airline>,Integer,Integer,List<String>> lowestDelaysAirlines=
        (airlins,year,k)->airlins.stream()
        .sorted((a1,a2)->(int)(getTotalDelay.apply(a1,year)-getTotalDelay.apply(a2,year)))
        .map(al -> al.getName()).limit(k)
        .collect(Collectors.toList());

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM