CompletableFutures и фильтрация на основе значений, которые находятся внутри

Я сейчас немного запутался, поэтому у меня есть метод, который должен возвращать CompletableFuture<List<A>>

внутри метода:

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

а convertToList() просто объединяет фьючерсы, чтобы преобразовать CompletableFuture<List<ComputableFuture<A>>> в CompletableFuture<List<A>>

В основном мое намерение состоит в том, чтобы отфильтровать нулевые значения, возникающие из optionalValue.orElse(null). И было бы легко сделать фильтр, прежде чем собирать все это, чтобы перечислить в последней строке, но если я использую его непосредственно перед .collect, он работает над CompletableFutures

Я подозреваю, что в моем коде можно много реструктурировать.

РЕДАКТИРОВАТЬ:

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

person Amir    schedule 21.11.2016    source источник
comment
Использовать Stream.of() и Stream.empty() ?   -  person akalikin    schedule 21.11.2016
comment
Не звоните .orElse(null), тогда вам не нужно тестировать null. Таким образом, тип параметра convertToList будет List<CompletableFuture<Optional<T>>>, и он может просто сделать .filter(Optional::isPresent).map(Optional::get) после join  -  person Holger    schedule 21.11.2016
comment
@Holger да, это возможность выполнять фильтрацию (либо по необязательным параметрам, либо по нулевым значениям) в convertToList, хотя я хотел бы делать все в одном месте, и convertToList может быть универсальной служебной функцией.   -  person Amir    schedule 21.11.2016
comment
Почему бы просто не написать служебную функцию, которая работает List<Option<T>> -> List<T>?   -  person Gene Taylor    schedule 22.11.2016


Ответы (1)


Лучшим способом, вероятно, было бы изменить convertToList() так, чтобы он возвращал не будущее списка, а поток:

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

Это будет более удобно для повторного использования, поскольку метод позволит лучше связать и не заставит вызывающую сторону работать со списком, в то же время позволяя легко получить список с помощью простого сбора.

Затем вы можете просто отфильтровать этот поток, чтобы удалить пустые опции:

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

    );

Вы даже можете немного улучшить это, изменив convertToFutureOfStream() так, чтобы он также принимал поток в качестве аргумента:

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

(к сожалению, это вызывает предупреждение о непроверенном назначении из-за массива универсальных типов)

Что затем дает

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

    );
person Didier L    schedule 07.12.2016