当前位置: 首页 > 知识库问答 >
问题:

Java从多个调用中收集CompletableFuture的结果

柴瀚
2023-03-14

我必须运行多个外部调用操作,然后以列表的形式获得结果。我决定使用completablefutureapi,而我准备的代码相当恶心:

示例

public class Main {
    public static void main(String[] args) {
        String prefix = "collection_";

        List<CompletableFuture<User>> usersResult = IntStream.range(1, 10)
                .boxed()
                .map(num -> prefix.concat("" + num))
                .map(name -> CompletableFuture.supplyAsync(
                        () -> callApi(name)))
                .collect(Collectors.toList());

        try {
            CompletableFuture.allOf(usersResult.toArray(new CompletableFuture[usersResult.size()])).get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        List<User> users = usersResult //the result I need
                .stream()
                .map(userCompletableFuture -> {
                    try {
                        return userCompletableFuture.get();
                    } catch (InterruptedException | ExecutionException e) {
                        e.printStackTrace();
                    }
                    return null;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }

    private static User callApi(String collection) {
        return new User(); //potentially time-consuming operation
    }
}

我有以下几个问题:

>

  • 我可以避免在流中重复try-catch块吗?在流中我将CompletableFuture映射到User?
  • 此代码是否可以不那么连续(如何避免等待所有的未来完成?)
  • 这样做可以吗(所有的未来都将在流中解决吗?):

    public class Main {
        public static void main(String[] args) {
            String prefix = "collection_";
    
            List<User> usersResult = IntStream.range(1, 10)
                    .boxed()
                    .map(num -> prefix.concat("" + num))
                    .map(name -> CompletableFuture.supplyAsync(
                            () -> callApi(name)))
                    .filter(Objects::nonNull)
                    .map(userCompletableFuture -> {
                        try {
                            return userCompletableFuture.get();
                        } catch (InterruptedException | ExecutionException e) {
                            e.printStackTrace();
                        }
                        return null;
                    })
                    .collect(Collectors.toList());
        }
    
        private static User callApi(String collection) {
            return new User(); //potentially time-consuming operation
        }
    }
    
  • 共有1个答案

    龙枫
    2023-03-14

    对于1.,您可以完全跳过ALLOF().GET()调用,因为您仍然在逐个等待所有未来。“

    对于2.,您可以通过执行以下操作来简化try-catch

    • 使用exceptionaly()在将来直接处理异常;
    • 使用join()而不是get()来避免检查异常(并且您知道不可能有异常)。

    对于3.你不能真的减少它的顺序,因为你至少需要步骤:创建所有的未来,然后处理他们的结果。

    如果您在一个流中执行所有操作,它将创建每个未来,然后在创建下一个之前立即等待它--这样您就会失去并行性。您可以使用并行流,但使用completablefutures并没有太大的好处。

    所以最终的代码是:

    List<CompletableFuture<User>> usersResult = IntStream.range(1, 10)
            .boxed()
            .map(num -> prefix.concat("" + num))
            .map(name -> CompletableFuture.supplyAsync(() -> callApi(name))
                .exceptionally(e -> {
                    e.printStackTrace();
                    return null;
                }))
            .collect(Collectors.toList());
    
    List<User> users = usersResult
            .stream()
            .map(CompletableFuture::join)
            .filter(Objects::nonNull)
            .collect(Collectors.toList());
    

    “注意,如果希望您的结果也是completablefuture ,则仍然需要allof()调用,例如。

    final CompletableFuture<List<User>> result =
            CompletableFuture.allOf(usersResult.stream().toArray(CompletableFuture[]::new))
                    .thenApply(__ -> usersResult
                            .stream()
                            .map(CompletableFuture::join)
                            .filter(Objects::nonNull)
                            .collect(Collectors.toList()));
    
     类似资料:
    • 我正在从数据库中读取pdf文件列表,对它们进行解析,并用它们执行一些任务 当我阅读这个pdf列表时,我发现从pdf中提取图像需要花费更多的时间,而且我不需要阻止我的主线程来阅读图像。所以我想在一个单独的线程中执行提取图像 我想从一个又一个pdf中读取图像,而不是一次将所有pdf加载到内存中(由于内存问题)。所以我只想要两条线;一个应该是主线程(从pdf中读取一些文本并执行其他操作),另一个应该是提

    • 我有以下问题: 我有一个如下的方法,一次只能收集500个名字: 我有一个1000个名字的列表,因此我想使用番石榴将这个列表划分为最大大小为500的子列表 并将其传递给请求以获取结果DTO列表。如何在 Java8 中执行此操作?

    • 我想通过调用来映射列表中的每个条目,它返回多个条目,然后将结果作为列表收集。 如果没有溪流,我会这样做: 我如何用流来完成这件事呢?这将给出一个编译错误:

    • 在阅读了Oracle站点上的这篇文章https://community.Oracle.com/docs/doc-995305之后,我将尝试实现“Some Two-to-One selection patterns”段落中描述的模式。这最后一类模式还包含二对一模式。但是这次不是执行一次下游元素,而是完成两个上游元素,当两个上游元素中的一个完成时执行下游元素。例如,当我们要解析域名时,这可能会证明非常

    • 问题内容: 我盯着一些命令性代码,试图将其转换为纯函数式样式。基本上有一个迭代的for循环,在该循环中,我检查3个谓词,并根据匹配的谓词填充3个谓词。输出集可以重叠。如何使用Java 8 Streams / map / filter /等以纯功能方式实现此目的? 问题答案: 最简单的解决方案(除了将所有内容保留为更容易之外)是创建三个单独的流: 如果有谓词列表,则可以创建相应的集合列表: 在此,结

    • 我有两个资源和,我可以用检索用户的产品。这很简单,我会返回一个JSON响应,如下所示: 现在,如果我想同时获得和他们的,该怎么办?我知道我可以使用在调用并返回如下数据: 但这是最佳实践吗?还是退回这样的东西更好: 所以我的问题是: 在一次呼叫中返回多个数据收集的最佳实践是什么