【问题标题】:Consuming Rest Service the functional way以功能方式消费 Rest Service
【发布时间】:2016-02-01 13:35:34
【问题描述】:

所以我很好奇如何使用 java8 流 API 更有效地重写以下代码。

public static List<FlightInfo> getResults(String origin,List<String> destinations)  {

    final String uri = "https://api.searchflight.com/;
    List<FlightInfo> results = new LinkedList<FlightInfo>();

    for(String destination:destinations) {


            RestTemplate restTemplate = new RestTemplate();

            String params = getParams(origin,destination);
            FlightInfo result = restTemplate.postForObject(uri+params,FlightInfo.class);

            results.add(result);
    }

    return results;

}

在这个方法完成它正在做的事情并且我收到 FLightInfo 对象列表后,我将它转换为一个流并将对其进行各种转换(分组依据等)。现在很明显这是一个长期运行的操作。此外,它实际上结合了对 Web 服务的多个 rest 调用,所以我在最后一次调用时已经获得了大部分数据,但在整个方法返回之前我不会开始处理它。

有没有办法让这一切变得更加被动?我可以立即返回一个流,并让该流处理数据的操作,因为它从管道中下来,或者这有点太多了?在 Java 8 中如何做到这一点。

【问题讨论】:

    标签: java rest java-8 lazy-evaluation java-stream


    【解决方案1】:

    这一切都取决于您何时需要结果。如果您希望它是连续的,那么下面这仍然是一种不错的方式,因为它很懒惰。但它会在终端操作时沸腾(比如在collect 期间)。

    public static Stream<FlightInfo> getResults(String origin,List<String> destinations)  {
        final String uri = "https://api.searchflight.com/";
        return destinations.stream().map(destination -> {
            RestTemplate restTemplate = new RestTemplate();
            String params = getParams(origin,destination);
            FlightInfo result = restTemplate.postForObject(uri+params,FlightInfo.class);
            return result;
        })    
    }
    

    或者,如果可以的话,我会使用 destinations.stream().parallel()。在大多数情况下,这是一个合理的结果。但是在您为其调用终端操作之前,它仍然不会开始并行处理它。这绝对是有道理的。

    但在我看来,您希望有一个生产者-消费者类型的东西。其中:

    public static CompletableFuture<List<FlightInfo>> getResults(String origin,List<String> destinations)  {
        final String uri = "https://api.searchflight.com/";
        List<CompletableFuture<FlightInfo>> collect = destinations
               .stream()
               .map(destination -> CompletableFuture.supplyAsync(() -> {
                     RestTemplate restTemplate = new RestTemplate();
                     String params = getParams(origin,destination);
                     FlightInfo result = restTemplate.postForObject(uri+params,FlightInfo.class);
                     return result;            
               })).collect(Collectors.toList());
        return sequence(collect);       //line-1 
    }
    
    public static <T> CompletableFuture<List<T>> sequence(List<CompletableFuture<T>> com) {
        return CompletableFuture.allOf(com.toArray(new CompletableFuture[com.size()]))
                .thenApply(v -> com.stream()
                                .map(CompletableFuture::join)
                                .collect(Collectors.toList())
                );
    }
    

    为了简单,在line-1 您可以简单地返回collect 而不是sequence(collect)。然后,您可以遍历列表以获取每个值。

    但是使用sequence,您只需担心一个 CompletableFuture 对象,然后您可以在完成后立即检查值。

    【讨论】:

    • 所以说可完成的未来方法也应与获取结果重叠处理是否正确。从表面上看,这实际上是映射到使用流的生产者消费者。在那种情况下,您会说这种特殊方法相对于实施生产者 - 消费者的好处(当然除了简洁和缺乏样板)。顺便说一句,我非常感谢提供的解决方案,我认为这很棒。我只是想更好地理解,这就是为什么要问这些问题。再次感谢
    • 上面的 CompletableFuture 为生产者消费者提供了一个很好的抽象。好吧,如果它看起来很难看,但如果上述模式的出现率高于代码库,那么我建议查看 github.com/ReactiveX/RxJava/wiki 。它提供了很好的抽象作为 Observables(你可以把它想象成一个流),一旦可用,你就可以获得结果。因为抽象是合理的,所以你对表现力有很强的把握
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-08
    • 2016-06-21
    • 2017-12-01
    • 1970-01-01
    • 2015-09-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多