我必须运行多个外部调用操作,然后以列表的形式获取结果。
我决定使用CompletableFuture
api,我准备的代码非常恶心:
示例:
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映射到用户?这样做是否正常(所有未来都会在流中解决?):
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
}
}
答案 0 :(得分:1)
对于1.,您可以完全跳过allOf().get()
来电,因为您无论如何都要一个接一个地等待所有未来.¹
对于2.,您可以通过执行以下操作来简化try-catch
:
exceptionally()
直接处理异常; join()
代替get()
以避免检查异常(并且您知道没有例外)。对于3.,由于您至少需要步骤,因此无法真正减少顺序:创建所有未来然后处理结果。
如果您在单个流中执行所有操作,它将创建每个未来,然后在创建下一个之前立即等待它 - 因此您将失去并行性。您可以使用并行流,但使用CompletableFuture
时没有多大好处。
所以最终的代码是:
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());
¹请注意,如果您希望结果也是allOf()
,则仍需要CompletableFuture<List<User>>
来电,例如
final CompletableFuture<List<User>> result =
CompletableFuture.allOf(usersResult.stream().toArray(CompletableFuture[]::new))
.thenApply(__ -> usersResult
.stream()
.map(CompletableFuture::join)
.filter(Objects::nonNull)
.collect(Collectors.toList()));
答案 1 :(得分:0)
或者,你可以放弃CompletableFuture
并使用parallelStream()
作为Didier提到的:
Optional<User> wrapApiCall(String name) {
try { return Optional.of(callApi(name)); }
catch (Exception e) {
e.printStackTrace();
return Optional.empty();
}
}
List<User> usersResult = IntStream.range(1, 10)
.boxed()
.parallelStream()
.map(num -> String.format("%s%d", prefix, num))
.map(this::wrapApiCall)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());