是否有资源限制在stream()。map()函数中进行REST调用?

时间:2017-09-12 10:29:35

标签: java-8 stream

我目前必须开发一个接口,在给定一个元素列表的情况下,我必须为每个元素调用一个端点,然后用每个响应创建一个对象。目前,没有办法将所有元素捆绑到一个单独的调用中,因此我不得不这样做。

从代码的角度来看,最简单的方法就是这样做:

List<ResponseDto> responses = list.stream().map(element -> {
    ServiceResponseDto dto = httpService.callWith(element);
    return new MyResponseDto(dto.getInfo(), otherInfo);
}).collect(Collectors.toList());

这样做有任何限制或代码味道吗?另一种选择是在这里使用forEach:

List<ResponseDto> responses = new ArrayList<>();
list.forEach(element -> {
    ServiceResponseDto dto = httpService.callWith(element);
    responses.add(new MyResponseDto(dto.getInfo(), otherInfo);
});

但是我没有看到资源利用方面的任何差异,我只是在forEach循环中创建副作用,只需使用第一种方法即可避免。

使代码更快的另一个方法是使用pararellStream但我的理解是我可以通过创建许多将等待IO的线程来使机器资源匮乏。

1 个答案:

答案 0 :(得分:1)

您可以将第一个建议中的步骤分开:

List<ResponseDto> responses = list.stream()
    .map(httpService::callWith)
    .map(dto -> new MyResponseDto(dto.getInfo(), otherInfo))
    .collect(Collectors.toList());

看起来有点干净。请记住,Stream是惰性的,因此您不必将所有逻辑放在一个map语句中 - 没有惩罚。

关于你的另一个问题,是的 - 不要使用并行流来阻止IO调用,因为它可以在公共池中保存线程,否则其他并行流可以使用它。

有一种机制允许您仍然使用线程而不必阻塞公共池。输入CompletableFuture

// imports elided, but all part of the JDK
ExecutorService fixedPool = Executors.newFixedThreadPool(16); // choose a good number - could be numberOfProcessors * 2

List<CompletableFuture<MyResponseDto>> responses = list.stream()
    .map(element -> CompletableFuture
          .supplyAsync(() -> httpService.callWith(element), fixedPool)
          .thenApply(dto -> new MyResponseDto(dto.getInfo(), otherInfo)))
    .collect(Collectors.toList());

// sequence futures so I only have to account for one future
CompletableFuture<List<MyResponseDto>> result = waitForAll(responses);

List<MyResponseDto> finalResult = result.join(); // try to put this code as late as you need it, since it will block the current thread

fixedPool.shutDown(); // also this, it will render the pool unusable anymore

序列功能如下所示。在函数式编程中,序列函数将List<T<U>>转换为<T<List<U>>。在这种情况下,TCompletableFutureU是数据。

// sequence futures
public static <T> CompletableFuture<List<T>> waitForAll(List<CompletableFuture<T>> futures) {
    CompletableFuture<Void> completedFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
    return completedFutures
        .thenApply(ignoreThis -> futures.stream()
            .map(CompletableFuture::join)
            .collect(Collectors.toList()));
}

我们需要重新实现序列函数,即使CompletableFutureallOf方法,因为它的返回类型是CompletableFuture<Void>(以满足不是你所有{的事实{1}} s可能具有相同的类型)所以我们自己动手推送我们的数据是同源/相同的类型。