我目前必须开发一个接口,在给定一个元素列表的情况下,我必须为每个元素调用一个端点,然后用每个响应创建一个对象。目前,没有办法将所有元素捆绑到一个单独的调用中,因此我不得不这样做。
从代码的角度来看,最简单的方法就是这样做:
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的线程来使机器资源匮乏。
答案 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>>
。在这种情况下,T
是CompletableFuture
,U
是数据。
// 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()));
}
我们需要重新实现序列函数,即使CompletableFuture
有allOf
方法,因为它的返回类型是CompletableFuture<Void>
(以满足不是你所有{的事实{1}} s可能具有相同的类型)所以我们自己动手推送我们的数据是同源/相同的类型。