Java 8 如何很好地使用CompletionStage集合进行allOf/AnyOf
目前,要对CompletionStage集合做一些简单的事情,需要跳过几个难看的障碍:Java 8 如何很好地使用CompletionStage集合进行allOf/AnyOf,java-8,completion-stage,Java 8,Completion Stage,目前,要对CompletionStage集合做一些简单的事情,需要跳过几个难看的障碍: public static CompletionStage<String> translate(String foo) { // just example code to reproduce return CompletableFuture.completedFuture("translated " + foo); } public static CompletionStage&
public static CompletionStage<String> translate(String foo) {
// just example code to reproduce
return CompletableFuture.completedFuture("translated " + foo);
}
public static CompletionStage<List<String>> translateAllAsync(List<String> input) {
List<CompletableFuture<String>> tFutures = input.stream()
.map(s -> translate(s)
.toCompletableFuture())
.collect(Collectors.toList()); // cannot use toArray because of generics Arrays creation :-(
return CompletableFuture.allOf(tFutures.toArray(new CompletableFuture<?>[0])) // not using size() on purpose, see comments
.thenApply(nil -> tFutures.stream()
.map(f -> f.join())
.map(s -> s.toUpperCase())
.collect(Collectors.toList()));
}
publicstaticcompletionstage转换(字符串foo){
//只是要复制的示例代码
返回CompletableFuture.completedFuture(“已翻译”+foo);
}
公共静态CompletionStage translateAllAsync(列表输入){
List tFutures=input.stream()
.map->translate(s)
.toCompletableFuture())
.collect(Collectors.toList());//由于创建泛型数组,无法使用toArray:-(
return CompletableFuture.allOf(tFutures.toArray(new CompletableFuture[0])//不是故意使用size(),请参见注释
.thenApply(nil->t未来.stream()
.map(f->f.join())
.map(s->s.toUpperCase())
.collect(Collectors.toList());
}
我想写的是:
public CompletionStage<List<String>> translateAllAsync(List<String> input) {
// allOf takes a collection< futures<X>>,
// and returns a future<collection<x>> for thenApply()
return XXXUtil.allOf(input.stream()
.map(s -> translate(s))
.collect(Collectors.toList()))
.thenApply(translations -> translations.stream()
.map(s -> s.toUpperCase())
.collect(Collectors.toList()));
}
public CompletionStage translateAllAsync(列表输入){
//allOf接受一个集合,
//并返回应用()的未来
返回XXXUtil.allOf(input.stream()
.map->translate(s))
.collect(收集器.toList())
.thenApply(翻译->翻译.stream()
.map(s->s.toUpperCase())
.collect(Collectors.toList());
}
关于CompletableFuture和转换为数组和连接的整个仪式都是样板,分散了对实际代码语义的注意力
在某些情况下,可能有一个allOf()版本返回一个Future
而不是Future
我可以自己尝试实现XXXUtil,但我想知道是否已经有一个成熟的第三方库来解决这个问题和类似的问题(比如Spotify的CompletableFutures)。如果是这样,我想看看这个库的等效代码作为答案
或者,也许上面发布的原始代码可以以不同的方式编写得更简洁
JUnit测试代码:
@Test
public void testTranslate() throws Exception {
List<String> list = translateAllAsync(Arrays.asList("foo", "bar")).toCompletableFuture().get();
Collections.sort(list);
assertEquals(list,
Arrays.asList("TRANSLATED BAR", "TRANSLATED FOO"));
}
@测试
public void testTranslate()引发异常{
List List=translatealasync(Arrays.asList(“foo”、“bar”)).toCompletableFuture().get();
集合。排序(列表);
资产质量(列表,
asList(“翻译的条”,“翻译的FOO”);
}
我刚刚查看了CompletableFuture.allOf的源代码,发现它基本上创建了一个节点二叉树,一次处理两个阶段。我们可以轻松实现类似的逻辑,而无需显式使用toCompletableFuture()
并一次性处理结果列表生成:
public static <T> CompletionStage<List<T>> allOf(
Stream<? extends CompletionStage<? extends T>> source) {
return allOf(source.collect(Collectors.toList()));
}
public static <T> CompletionStage<List<T>> allOf(
List<? extends CompletionStage<? extends T>> source) {
int size = source.size();
if(size == 0) return CompletableFuture.completedFuture(Collections.emptyList());
List<T> result = new ArrayList<>(Collections.nCopies(size, null));
return allOf(source, result, 0, size-1).thenApply(x -> result);
}
private static <T> CompletionStage<Void> allOf(
List<? extends CompletionStage<? extends T>> source,
List<T> result, int from, int to) {
if(from < to) {
int mid = (from+to)>>>1;
return allOf(source, result, from, mid)
.thenCombine(allOf(source, result, mid+1, to), (x,y)->x);
}
return source.get(from).thenAccept(t -> result.set(from, t));
}
公共静态完成阶段allOf(
Streamrelated:,为什么有人会让toUpperCase()
的计算依赖于所有不相关的异步任务的完成?顺便说一下,。所以只需使用translationFutures.toArray(new CompletableFutures[0])
而不是translationFutures.toArray(new CompletableFutures[translationFutures.size()]))
感谢您提供有关数组预大小的提示。我使用的示例本身并不有用,它只是我可以很快想出的,让其他人重现与我实际问题类似的结构。在我的实际情况中,它不是字符串,单个异步调用是对不同服务器的远程调用。我可能会认为这个答案是正确的在某种意义上或者在算法上实现了我想要的。尽管我有点担心单元测试的数量,特别是在竞争条件下。我想对于任何一种方法来说都是类似的。
public static CompletionStage<List<String>> translateAllAsync(List<String> input) {
return allOf(input.stream().map(s -> translate(s)))
.thenApply(list -> list.stream()
.map(s -> s.toUpperCase())
.collect(Collectors.toList()));
}
public static CompletionStage<List<String>> translateAllAsync(List<String> input) {
return allOf(input.stream().map(s -> translate(s).thenApply(String::toUpperCase)));
}
@Test
public void testTranslate() throws Exception {
List<String> list = translateAllAsync(Arrays.asList("foo", "bar")).toCompletableFuture().get();
assertEquals(list, Arrays.asList("TRANSLATED FOO", "TRANSLATED BAR"));
}