CompletableFutures and filtering based on values that are inside

丶灬走出姿态 提交于 2019-12-11 04:32:53

问题


I'm in a bit of confusion right now, so I have a method that should return CompletableFuture<List<A>>

inside the method is:

CompletableFuture<List<String>> toReturn = asyncCall().thenApply(....)
.thenCompose(listOfStuff -> convertToList(listOfStuff.stream().map(
     key -> asyncCall2(key)
        .thenApply(optionalValue -> optionalValue.orElse(null))
).collect(Collectors.toList()));

and convertToList() simply joins futures to convert CompletableFuture<List<ComputableFuture<A>>> into CompletableFuture<List<A>>

Basically my intention is to filter null values that emerge from optionalValue.orElse(null) And it would be easy to do filter before collecting it all to list in the last line, but if I use it just before .collect it is working over CompletableFutures

I suspect there's a lot of restructuring I can do in my code.

EDIT:

private<T> CompletableFuture<List<T>> convertToList(List<CompletableFuture<T>> toConvert) {
    return CompletableFuture.allOf(toConvert.toArray(new CompletableFuture[toConvert.size()]))
            .thenApply(v -> toConvert.stream()
                    .map(CompletableFuture::join)
                    .collect(Collectors.toList())
            );
}

回答1:


The best way would probably be to change convertToList() so that it does not return a future of list, but of stream instead:

private <T> CompletableFuture<Stream<T>> convertToFutureOfStream(List<CompletableFuture<T>> toConvert) {
    return CompletableFuture.allOf(toConvert.stream().toArray(CompletableFuture[]::new))
            .thenApply(
                    v -> toConvert.stream()
                            .map(CompletableFuture::join)
            );
}

This will be more reusable as the method will allow better chaining and will not force the caller to work with a list, while still allowing to easily get a list with a simple collect.

You can then simply filter that stream to remove empty optionals:

CompletableFuture<List<String>> toReturn = asyncCall()
    .thenCompose(listOfStuff -> convertToFutureOfStream(
            listOfStuff.stream()
                    .map(this::asyncCall2)
                    .collect(Collectors.toList())
        )
        .thenApply(stream ->
                stream.filter(Optional::isPresent)
                        .map(Optional::get)
                        .collect(Collectors.toList())
        )

    );

You can even improve this a little further by changing convertToFutureOfStream() to take a stream as argument as well:

private <T> CompletableFuture<Stream<T>> convertToFutureOfStream(Stream<CompletableFuture<T>> stream) {
    CompletableFuture<T>[] futures = stream.toArray(CompletableFuture[]::new);
    return CompletableFuture.allOf(futures)
            .thenApply(v -> Arrays.stream(futures).map(CompletableFuture::join));
}

(unfortunately this raises an unchecked assignment warning because of the array of generic types)

Which then gives

CompletableFuture<List<String>> toReturn = asyncCall()
    .thenCompose(listOfStuff -> convertToFutureOfStream(
                listOfStuff.stream().map(this::asyncCall2)
            )
        .thenApply(stream ->
                stream.filter(Optional::isPresent)
                        .map(Optional::get)
                        .collect(Collectors.toList())
        )

    );


来源:https://stackoverflow.com/questions/40720461/completablefutures-and-filtering-based-on-values-that-are-inside

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